6 El lenguaje de programación D
7 ============================================================================
11 ----------------------------------------------------------------------------
13 D_ es un lenguaje de programación relativamente joven. Nació en 1999 y el
14 2 de enero de 2007 salió su `versión 1.0`__. Poco tiempo después se continúo
15 el desarrollo del lenguaje en la `versión 2.0`__, que pasó a ser considerada
16 estable aproximadamente en junio de 2010 con el lanzamiento del libro "The
17 D Programming Language" [ALX10]_, pero aún es un trabajo en progreso.
22 El lenguaje fue diseñado e implementado por `Walter Bright`_, desarrollador
23 principal de Zortech C++, uno de los primeros compiladores de C++ que
24 compilaba a código nativo, y está fuertemente influenciado por éste. Sin
25 embargo toma muchos conceptos de otros lenguajes de más alto nivel, como Java_
26 o incluso lenguajes dinámicos como Perl_, Python_ y Ruby_.
28 El origen del lenguaje está plasmado en su sitio web, en donde se cita
31 It seems to me that most of the "new" programming languages fall into one
32 of two categories: Those from academia with radical new paradigms and those
33 from large corporations with a focus on RAD and the web. Maybe it's time
34 for a new language born out of practical experience implementing compilers.
36 Esto podría traducirse como:
38 Parece que la mayoría de los lenguajes de programación "nuevos" caen en
39 2 categorías: aquellos académicos con nuevos paradigmas radicales
40 y aquellos de grandes corporaciones con el foco en el desarrollo rápido
41 y web. Tal vez es hora de que nazca un nuevo lenguaje de la experiencia
42 práctica implementando compiladores.
44 La versión 1.0 fue más bien una etiqueta arbitraria que un indicador real de
45 estabilidad y completitud. Luego de liberarse se siguieron agregando nuevas
46 características al lenguaje hasta que se empezó el desarrollo en paralelo de
47 la versión 2.0 al introducirse el concepto de inmutabilidad y funciones
48 *puras* [#dpure]_ (a mediados de 2007).
50 .. [#dpure] Por funciones *puras* en D_ se entiende que no tienen efectos
51 colaterales. Es decir, una función pura siempre que se llame con la misma
52 entrada producirá el mismo resultado. Esto es análogo a como funcionan los
53 lenguajes funcionales en general, abriendo la puerta a la programación de
54 estilo funcional en D_.
56 A partir de este momento la versión 1.0 quedó *teóricamente* congelada,
57 introduciendo solo cambios que arreglen errores (*bug fixes*), agregando
58 nuevas características solamente en la versión 2.0 del lenguaje. La realidad
59 es que se hicieron cambios incompatibles a la versión 1.0 del lenguaje en
60 reiteradas ocasiones, pero se fue tendiendo a cada vez introducir menos
61 cambios incompatibles. Sin embargo al día de hoy el compilador de referencia
62 sigue teniendo algunas características presentes en la especificación del
63 lenguaje sin implementar, por lo que todavía no hay una implementación
64 completa de la versión 1.0 del lenguaje.
66 El lenguaje ha sido, hasta el desarrollo de la versión 2.0 al menos, un
67 esfuerzo unipersonal de `Walter Bright`_, dados sus problemas a la hora de
68 delegar o aceptar contribuciones. Esto motivó a la comunidad de usuarios de D_
69 a crear bibliotecas base alternativas a la estándar (llamada Phobos_) en las
70 cuales se pudiera trabajar sin las trabas impuestas por el autor del lenguaje.
72 En este contexto nacen primero Mango_ y luego Ares_. Mango_ fue creada por
73 Kris Macleod Bell a principios de 2004 como una biblioteca que provee
74 servicios básicos de entrada/salida (o *I/O* de *input/output* en inglés) de
75 alto rendimiento. Siendo estos servicios algo básico lo más natural hubiera
76 sido que se encuentren en la biblioteca estándar de D_ pero por las
77 dificultades para contribuir a ésta, se desarrolla como una biblioteca
78 separada. A mediados de 2004 Sean Kelly crea Ares_ , con las mismas
79 motivaciones pero con la intención de crear una biblioteca base (conocida en
80 inglés como *runtime*) que incluye los servicios básicos que necesita el
81 lenguaje (información de tipos, manejo de excepciones e hilos, creación
82 y manipulación de objetos, recolector de basura, etc.). Al poco tiempo de
83 liberarse Ares_, Mango_ empieza a utilizarla como biblioteca base.
85 Para comienzos de 2006, se empieza a trabajar en la combinación de ambas
86 bibliotecas para lograr una biblioteca estándar alternativa con un alto grado
87 de cohesión. Finalmente a principios de 2007, coincidiendo por casualidad con
88 la aparición de `D 1.0`_, se anuncia el resultado de este combinación bajo el
89 nombre de Tango_, proveyendo una alternativa completa y madura a la biblioteca
90 estándar de D_ Phobos_. A principios de 2008 los principales desarrolladores
91 de Tango_ (Kris Bell, Sean Kelly, Lars Ivar Igesund y Michael Parker) publican
92 el libro llamado `Learn to Tango with D`_ [BKIP08]_.
94 Esto por un lado fue un gran avance porque dio un impulso muy considerable al
95 lenguaje pero por otro un gran retroceso, porque todavía al día de hoy `D
96 1.0`_ tiene dos bibliotecas base, una estándar pero de peor calidad, menos
97 mantenida y usada; y una alternativa de mayor calidad y apertura a la
98 comunidad (pero no estándar). El peor problema es que ambas son
99 **incompatibles**, por lo que un programa hecho con Tango_ no funciona con
100 Phobos_ y viceversa (a menos que el programador haya invertido una cantidad de
101 tiempo considerable en asegurarse que funcione con ambas).
103 Esto hace que la compatibilidad de programas y bibliotecas esté muy
104 fragmentada entre las dos bibliotecas base. Si bien no parece que vaya a haber
105 solución alguna a este problema para `D 1.0`_, `D 2.0`_ va en camino
106 a solucionar este problema ya que utiliza DRuntime_, un nuevo intento de Sean
107 Kelly por proveer una biblioteca *runtime* bien organizada y mantenida, que es
108 una adaptación de la biblioteca *runtime* de Tango_ a `D 2.0`_. Sin embargo
109 Tango_ no fue adaptada a `D 2.0`_ todavía, y no hay muchas perspectivas de que
110 sea portada en algún momento, por un lado porque en general la comunidad sigue
111 fragmentada entre muchos usuarios de `D 1.0`_ que no están contentos con los
112 cambios introducidos en `D 2.0`_, en su mayoría usuarios de Tango_, y que no
113 planean migrar a esa versión; y por otro porque el desarrollo de Phobos_ 2.0
114 se ha abierto mucho y tiene muchos colaboradores, por lo tanto la mayor parte
115 de la gente que utiliza `D 2.0`_ está contenta con el estado de Phobos_ 2.0.
119 ----------------------------------------------------------------------------
121 D_ es un lenguaje de programación con sintaxis tipo C, multi-paradigma,
122 compilado, con *tipado* fuerte y estático, buenas capacidades tanto de
123 programación de bajo nivel (*system programming*) como de alto nivel; además
124 es compatible de forma binaria con C (se puede enlazar código objeto C con
125 código objeto D). Con estas características, D_ logra llenar un vacío
126 importante que hay entre lo lenguajes de alto bajo nivel y los de alto nivel
127 [BKIP08]_. Si bien tiene herramientas de muy bajo nivel, que por lo tanto son
128 muy propensas a errores, da una infinidad de mecanismos para evitar el uso de
129 estas herramientas a menos que sea realmente necesario. Además pone mucho
130 énfasis en la programación confiable, para lo cual provee muchos mecanismos
131 para detectar errores en los programas de forma temprana.
133 Si puede pensarse en C++ como un "mejor C", podría decirse que D_ es un "mejor
134 C++", ya que el objetivo del lenguaje es muy similar a C++, pero implementa
135 muchas características que jamás pudieron entrar en el estándar de C++ y lo
136 hace de una forma mucho más limpia, ya que no debe lidiar con problemas de
137 compatibilidad hacia atrás, y cuenta con la experiencia del camino recorrido
138 por C++, pudiendo extraer de él los mejores conceptos pero evitando sus
141 Una gran diferencia con C++ es que el análisis sintáctico (*parsing*) se puede
142 realizar sin ningún tipo de análisis semántico, dado que a diferencia de éstos
143 su gramática es libre de contexto (*context-free grammar*). Esto acelera
144 y simplifica considerablemente el proceso de compilación [WBB10]_ [DWOV]_.
146 Otra gran diferencia es que D_ decide incluir recolección de basura como parte
147 del lenguaje, mientras que en el comité de estandarización de C++ nunca se
148 llegó a un consenso para su incorporación.
151 Características del lenguaje
152 ----------------------------------------------------------------------------
154 A continuación se enumeran las principales características de D_, agrupadas
155 por unidades funcionales o paradigmas que soporta [DWLR]_:
161 Programación genérica y meta-programación
162 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
164 La programación genérica se trata de la capacidad de poder desarrollar
165 algoritmos y estructuras independientes de los tipos que manipulan (pero de
166 forma segura o *type-safe*). Esto fue muy popularizado por C++ gracias a su
167 soporte de plantillas (*templates*) y luego otros lenguajes como Java_ y `C#`_
168 lo siguieron. Sin embargo otros lenguajes proveen formas más avanzadas de
169 programación genérica, gracias a sistemas de tipos más complejos (como
172 La meta-programación se refiere en general a la capacidad de un lenguaje para
173 permitir generar código dentro del mismo programa de forma automática. Esto
174 permite evitar duplicación de código y fue también muy popularizado por el
175 soporte de *templates* de C++, aunque muchos otros lenguajes tienen mejor
176 soporte de meta-programación, en especial los lenguajes dinámicos (como
179 D_ provee las siguientes herramientas para realizar programación genérica
182 ``if`` estático (``static if``)
183 Esta construcción es similar a la directiva del preprocesador de C/C++
184 ``#if``, pero a diferencia de éste, el ``static if`` de D_ tiene acceso
185 a todos los símbolos del compilador (constantes, tipos, variables, etc)
190 static if ((void*).sizeof == 4)
191 pragma(msg, "32 bits");
193 Inferencia de tipos básica implícita y explícita (mediante ``typeof``)
194 Si no se especifica un tipo al declarar una variable, se infiere a partir
195 del tipo de su valor de inicialización [DWIN]_.
199 static i = 5; // i es int
200 const d = 6.0; // d es double
201 auto s = "hola"; // s es string (que es un alias de char[])
203 Mediante el uso de ``typeof`` se puede solicitar el tipo de una expresión
208 typeof(5 + 6.0) d; // d es double
210 Iteración sobre colecciones (``foreach``)
211 Cualquier tipo de colección (arreglos estáticos y dinámicos, arreglos
212 asociativos, clases, estructuras o delegados) puede ser iterada mediante la
213 sentencia ``foreach`` [DWFE]_.
217 int[] a = [ 1, 2, 3 ];
223 Tanto clases como funciones pueden ser generalizadas. Esto permite desarrollar
224 algoritmos genéricos sin importar el tipo de los datos de entrada, siempre
225 y cuando todos los tipos tengan una *interfaz* común. Esto también es
226 conocido como *polimorfismo en tiempo de compilación*, y es la forma más
227 básica de programación genérica [DWTP]_.
231 T sumar(T)(T x, T y) { return x + y; }
232 auto i = sumar!(int)(5, 6); // i == 11
233 auto f = sumar!(float)(5, 6); // j == 11.0f
235 Además se pueden definir bloques de declaraciones generalizadas (esto no
236 es posible en C++), permitiendo instanciar dicho bloque con parámetros
237 particulares. Esto sirve como un mecanismo para la reutilización de código,
238 ya que puede incluirse un mismo bloque en distintos lugares (por ejemplo
239 clases). Un bloque generalizado puede verse como una especie de módulo.
243 template bloque(T, U) {
248 bloque!(int, float).x = 5;
249 float f = bloque!(int, float).foo(7);
251 La utilidad más prominente de los bloques generalizados se da al
252 acompañarse de *mixins*.
254 Además las *templates* de D_ tienen las siguientes características
257 Instanciación implícita de funciones generalizadas
258 El lenguaje es capaz de deducir los parámetros siempre que no hayan
263 auto i = sumar(5, 6); // i == 11
264 auto f = sumar(5.0f, 6.0f); // f == 11.0f
266 Especialización explícita y parcial de *templates*
267 La especialización de *templates* consiste, al igual que en C++, en
268 proveer una implementación especializada para un tipo de dato (o valor)
269 de los parámetros. Especialización parcial se refiere a la capacidad
270 de especializar un parámetro a través de un subtipo. Por ejemplo, se
271 puede especializar un *template* para cualquier tipo de puntero, o para
272 cualquier tipo de arreglo dinámico, sin necesidad de especificar el tipo
273 al que apunta dicho puntero o el tipo almacenado por el arreglo.
275 Ejemplo de especialización::
277 T sumar(T)(T x, T y) { return x + y; }
278 T sumar(T: int)(T x, T y) { return x + y + 1; }
279 auto i = sumar(5, 6); // i == 12
280 auto f = sumar(5.0f, 6.0f) // f == 11.0f
282 Ejemplo de especialización parcial::
284 T sumar(T)(T x, T y) { return x + y; }
285 T sumar(T: T*)(T x, T y) { return *x + *y; }
287 auto i = sumar(&x, &y); // i == 11
288 float v = 5.0f, w = 6.0f;
289 auto f = sumar(&v, &w); // f == 11.0f
291 Tipos, valores (incluyendo *strings*) y *templates* como parámetros
292 Este es otro bloque de construcción importantísimo para la programación
293 genérica en D, ya que combinando *templates* que toman *strings* como
294 parámetro en combinación con *string mixins* pueden hacerse toda clase
299 template hash(string s, uint so_far=0) {
300 static if (s.length == 0)
303 const hash = hash!(s[1 .. length], so_far * 11 + s[0]);
305 string s = hash!("hola"); // calculado en tiempo de compilación
307 Cantidad de parámetros variables para *templates*
308 Esta característica permite implementar tuplas y otros algoritmos que
309 inherentemente deben tomar una cantidad variable de parámetros en tiempo
314 double sumar(T...)(T t) {
320 double d = sumar(1, 2.0, 3.0f, 4l); // d == 10.0
322 *CTFE* (*compile-time function execution*)
323 Si una función cumple ciertas reglas básicas (como por ejemplo no tener
324 efectos colaterales) puede ser ejecutada en tiempo de compilación en vez de
325 tiempo de ejecución. Esto permite hacer algunos cálculos que no cambian de
326 ejecución en ejecución al momento de compilar, mejorando el rendimiento
327 o permitiendo formas avanzadas de meta-programación. Esta característica se
328 vuelve particularmente útil al combinarse con *string mixins* [DWCF]_.
332 int factorial(int n) {
336 return n * factorial(n - 1);
338 static int x = factorial(5); // calculado en tiempo de compilación
339 int x = factorial(5); // calculado en tiempo de ejecución
341 Esta característica es muy importante para evitar la duplicación de código.
343 *Mixins*, incluyendo *string mixins*
344 La palabra *mixin* tiene significados distintos en varios lenguajes de
345 programación. En D_ *mixin* significa tomar una secuencia arbitraria de
346 declaraciones e insertarla en el contexto (*scope*) actual. Esto puede
347 realizarse a nivel global, en clases, estructuras o funciones. Esto sirve
348 como un mecanismo para evitar duplicación de código que puede ser
349 introducida por la falta de herencia múltiple [DWMT]_.
354 mixin bloque!(int, float);
358 float f = a.foo(a.x);
361 mixin bloque!(long, double);
365 double d = a.foo(a.x);
367 *String mixin* se refiere a la capacidad de *incrustar* un *string* que
368 contenga un fragmento de código en un programa como si este fragmento
369 hubiera sido escrito en el código fuente directamente por el programador.
370 Esto permite hacer manipulaciones arbitrariamente complejas en combinación
371 con funciones ejecutadas en tiempo de compilación [DWME]_ [DWMX]_.
375 string generar_sumar(string var_x, string var_y) {
376 return "return " ~ var_x ~ " + " ~ var_y ~ ";";
379 int sumar(int a, int b) {
380 mixin(generar_sumar!("a", "b"));
384 Las *expresiones ``is``* permiten la compilación condicional basada en las
385 características de un tipo [DWIE]_.
390 static if (is(T == class))
396 Esto provee además una forma simple de reflexión en tiempo de compilación.
402 Programación de bajo nivel (*system programming*)
403 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
405 Por programación de bajo nivel nos referimos a la capacidad de un lenguaje de
406 manipular el hardware directamente, o al menos la memoria. C es probablemente
407 el lenguaje de bajo nivel más popular, seguido por C++.
409 D_ presenta muchas características de bajo nivel:
411 Compila a código de máquina nativo
412 Los programas generados por D_ no son interpretados ni necesitan una
413 máquina virtual como otros lenguajes de más alto nivel como Java_, `C#`_,
414 Python_, etc [DWOV]_.
417 Provee acceso directo al *hardware* y la posibilidad de utilizar cualquier
418 característica de éste que no esté disponible en el lenguaje.
420 Una ventaja sobre C y C++ es que el lenguaje *assembly* utilizado dentro de
421 D_ está especificado, por lo que se puede mantener la portabilidad entre
422 compiladores incluso cuando se utiliza *assembly* (mientras que no se
423 cambie de arquitectura, por supuesto) [DWIA]_.
426 Al igual que C y C++, D_ provee la flexibilidad del uso de ``goto``
430 Soporta todos los tipos de C y es ABI [#abi]_ compatible con éste. Esto
431 permite enlazar archivos objeto estándar de C y D_ en un mismo programa.
432 Además permite interoperar con C a través de ``extern (C)`` [DWCC]_.
434 .. [#abi] Interfaz de Aplicación Binaria (del inglés *Application Binary
439 extern (C) printf(const char* format, ...);
440 printf("3 + 5 == %d\n", 3 + 5); // llama al printf de C
442 Manejo de memoria explícito
443 Permite asignar estructuras en el *stack* o en el *heap*, haciendo uso de
444 los servicios del sistema operativo o la biblioteca estándar de C [DWMM]_.
446 Objetos y arreglos *livianos*
447 Por objetos *livianos* se entiende no-polimórficos. Es decir, un
448 agrupamiento de variables análogo al ``struct`` de C, sin tabla virtual ni
449 otro tipo de *overhead*. Los arreglos *livianos* son arreglos estáticos
450 como en C, cuyo tamaño es fijo, también sin ningún tipo de *overhead* como
451 C. Además puede asignarse un arreglo dinámicamente usando ``malloc()``
452 y utilizar el operador ``[]`` para accederlo [DWST]_ [DWCL]_.
454 Esto también permite interoperar con C, ya que pueden definirse ``structs``
455 y arreglos que pueden ser intercambiados con dicho lenguaje sin problemas.
464 void* malloc(size_t);
465 size_t strlen(const char *);
466 int gettimeofday(timeval *, void *);
468 char* s = cast(char*) malloc(2);
471 size_t l = strlen(s); // l == 1
473 gettimeofday(&tv, null);
476 La :ref:`d_generic` permite realizar muchas optimizaciones ya que se
477 resuelve en tiempo de compilación y por lo tanto aumenta el rendimiento en
478 la ejecución [DWTP]_.
480 Número de punto flotante de 80 bits
481 El tipo ``real`` de D_ tiene precisión de 80 bits si la plataforma lo
482 soporta (por ejemplo en i386) [DWTY]_.
484 Control de alineación de miembros de una estructura
485 Mediante ``align`` se puede especificar la alineación a tener en una
491 struct paquete_de_red {
495 // paquete_de_red.sizeof == 3
501 Programación de alto nivel
502 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
504 Programación de alto nivel se refiere a construcciones más avanzadas que una
505 sentencia para iterar; expresiones con una semántica más ricas que proveen de
506 mayor expresividad al programador o le permiten focalizarse de mejor manera
507 en los algoritmos independizándose del *hardware* o de como funciona una
508 computadora. Es exactamente el opuesto a :ref:`d_low_level`.
510 En general estas características tienen como efecto secundario una mejora de la
511 productividad de los programadores. D_ adopta herramientas de muchos lenguajes
512 de alto nivel, como Java_ y Python_, por ejemplo:
514 Manejo automático de memoria
515 Al igual que C/C++ y prácticamente cualquier lenguaje imperativo maneja
516 automáticamente el *stack*, pero a diferencia de la mayoría de los
517 lenguajes de bajo nivel, D_ permite manejar el *heap* de manera automática
518 también a través de un *recolector de basura* [DWGC]_.
520 Sistema de paquetes y módulos (similar a Java_ o Python_)
521 Un módulo es una unidad que agrupa clases, funciones y cualquier otra
522 construcción de lenguaje. Un paquete es una agrupación de módulos. D_
523 asocia un módulo a un archivo fuente (y un archivo objeto cuando éste es
524 compilado) y un paquete a un directorio. A diferencia de C/C++ no necesita
525 de un preprocesador para incluir declaraciones de otros *módulos* (en C/C++
526 no existe el concepto de módulo, solo de unidades de compilación) [DWMO]_.
547 f(); // ejecuta b.f()
549 Funciones y delegados
550 Las funciones pueden ser sobrecargadas (funciones con el mismo nombre pero
551 distinta cantidad o tipo de parámetros), pueden especificarse argumentos de
552 entrada, salida o entrada/salida, argumentos por omisión o argumentos
553 evaluados de forma perezosa (*lazy*). Además pueden tener una cantidad de
554 argumentos variables pero manteniendo información de tipos (más seguro que
557 Los *delegados* son punteros a función con un contexto asociado. Este
558 contexto puede ser un objeto (en cuyo caso la función es un método) o un
559 *stack frame* (en cuyo caso la función es una función anidada).
561 Además de esto los delegados son ciudadanos de primera clase
562 [#1stclasscity]_, disponiendo de forma literal (delegado anónimo), lo que
563 permite construcciones de alto nivel muy conveniente. Los argumentos
564 evaluados de forma perezosa no son más que un delegado que se ejecuta solo
567 .. [#1stclasscity] Por ciudadano de primera clase se entiende que se trata
568 de un tipo soportado por completo por el lenguaje, disponiendo de
569 expresiones literales anónimas, pudiendo ser almacenados en variables,
570 estructuras de datos, teniendo una identidad intrínseca, más allá de un
571 nombre dado, etc. En realidad los arreglos asociativos no pueden ser
572 expresados como literales anónimos pero sí tienen una sintaxis especial
573 soportada directamente por el lenguaje.
577 bool buscar(T[] arreglo, T item, bool delegate(T x, T y) igual) {
590 bool encontrado = buscar(personas, p,
591 (Persona x, Persona y) {
592 return x.nombre == y.nombre;
596 Arreglos *dinámicos* y arreglos asociativos
597 Los arreglos *dinámicos* son arreglos de longitud variable manejados
598 automáticamente por el lenguaje (análogos al ``std::vector`` de C++).
599 Soportan concatenación (a través del operador ``~``), rebanado o *slicing*
600 (a través del operador ``[x..y]``) y chequeo de límites (*bound checking*)
603 Los arreglos asociativos (también conocidos como *hashes* o diccionarios)
604 también son provistos por el lenguaje [DWAA]_.
606 Ambos son ciudadanos de primera clase, disponiendo de forma literal.
610 int[] primos = [ 2, 3, 5, 7, 11, 13, 17, 19 ];
611 primos ~= [ 23, 29 ];
612 auto menores_que_10 = primos[0..4]; // [ 2, 3, 5, 7 ]
614 agenda["Pepe"] = 5555_1234;
617 Al igual que los delegados y arreglos dinámicos y asociativos, los
618 *strings* son ciudadanos de primera clase, teniendo forma literal y siendo
619 codificados en UTF-8/16/32. Son un caso particular de arreglo dinámico y es
620 posible utilizarlos en sentencias ``switch``/``case`` [DWSR]_.
633 ``typedef`` y ``alias``
634 El primero define un nuevo tipo basado en otro. A diferencia de C/C++ el
635 tipo original no puede ser implícitamente convertido al tipo nuevo (excepto
636 valores literales), pero la conversión es válida en el otro sentido
637 (similar a los ``enum`` en C++). Por el contrario, ``alias`` es análogo al
638 ``typedef`` de C/C++ y simplemente es una forma de referirse al mismo tipo
639 con un nombre distinto [DWDC]_.
648 foo(i); // error, no compila
653 Documentación embebida
654 D_ provee un sistema de documentación embebida, análogo a lo que proveen
655 Java_ o Python_ en menor medida. Hay comentarios especiales del código que
656 pueden ser utilizados para documentarlo de forma tal que luego el
657 compilador pueda extraer esa información para generar un documento [DWDO]_.
660 D_ soporta números complejos como ciudadanos de primera clase. Soporta
661 forma literal de números imaginarios y complejos [DWTY]_.
667 cfloat c = re + im; // c == 1.0 + 5.0i
671 Programación orientada a objetos
672 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
674 La orientación a objetos es probablemente el paradigma más utilizado en la
675 actualidad a la hora de diseñar e implementar un programa. D_ provee muchas
676 herramientas para soportar este paradigma de forma confiable. Entre las
677 características más salientes se encuentran:
680 Objetos polimórficos como los de cualquier lenguaje con orientación real
681 a objetos. Estos objetos poseen una tabla virtual para despacho dinámico,
682 todos los métodos son virtuales a menos que se indique lo contrario
683 y tienen semántica de referencia [#drefsem]_. Estos objetos tienen un
684 *overhead* comparados a los objetos *livianos* pero aseguran una semántica
685 segura para trabajar con orientación a objetos, evitando problemas con los
686 que se enfrenta C++ (como *slicing* [#dslicing]_) debido a que permite
687 semántica por valor [#dvalsem]_ [DWCL]_.
689 .. [#drefsem] Semántica de referencia significa que el tipo es tratado como
690 si fuera un puntero. Nunca se hacen copias del objeto, siempre se pasa
692 .. [#dslicing] Este problema se da en C++ cuando se pasa una clase derivada
693 a una función que acepta una clase base por valor como parámetro. Al
694 realizarse una copia de la clase con el constructor de copia de la clase
695 base, se pierden (o *rebanan*) los atributos de la clase derivada, y la
696 información de tipos en tiempo de ejecución (*RTTI*).
697 .. [#dvalsem] Semántica de valor significa que el tipo es tratado como si
698 fuera un valor concreto. En general se pasa por valor y se hacen copias
699 a menos que se utilice explícitamente un puntero.
701 D_ además soporta tipos de retorno covariantes para funciones virtuales.
702 Esto significa que una función sobreescrita por una clase derivada puede
703 retornar un tipo que sea derivado del tipo retornado por la función
704 original sobreescrita [DWFU]_.
712 A test() { return null; }
716 B test() { return null; } // sobreescribe y es covariante con Foo.test()
720 D_ no soporta herencia múltiple pero sí interfaces. Una interfaz es
721 básicamente una tabla virtual, una definición de métodos virtuales que debe
722 proveer una clase. Las interfaces no proveen una implementación de dichos
723 métodos, ni pueden tener atributos. Esto simplifica mucho el lenguaje y no
724 se pierde flexibilidad porque puede conseguirse el mismo efecto de tener
725 herencia múltiple a través de interfaces y *mixins* para proveer una
726 implementación o atributos en común a varias clases que implementan la
727 misma interfaz [DWIF]_.
729 Sobrecarga de operadores
730 La sobrecarga de operadores permite que un objeto tenga una sintaxis
731 similar a un tipo de dato nativo. Esto es muy importante además para la
732 programación genérica [DWOO]_.
735 Al igual que C (con respecto a ``struct``) y C++, pueden anidarse clases
736 dentro de clases. D_ sin embargo provee la posibilidad de acceder
737 a atributos de la instancia exterior desde la anidada [DWNC]_.
745 return m; // ok, puede acceder a un miembro de Exterior
750 Esto tiene un pequeño *overhead* ya que la clase ``Anidada`` debe guardar
751 un puntero a la clase ``Exterior``. Si no se necesita este comportamiento
752 es posible evitar este *overhead* utilizando ``static``, en cuyo caso solo
753 puede acceder a atributos estáticos de la clase ``Exterior``.
760 static class Anidada {
762 //return m; // error, miembro de Exterior
763 return n; // ok, miembro estático de Exterior
769 Propiedades (*properties*)
770 En D_ se refiere a funciones miembro que pueden ser tratadas
771 sintácticamente como campos de esa clase/estructura [DWPR]_.
776 int data() { return _data; } // propiedad de lectura
777 int data(int value) { return _data = value; } // de escritura
781 f.data = 1; // llama a f.data(1)
782 int i = f.data; // llama a f.data()
784 Además tipos nativos, clases, estructuras y expresiones tienen
785 *properties* predefinidos, por ejemplo:
788 Tamaño ocupado en memoria (ejemplo: ``int.sizeof`` -> 4).
791 Valor de inicialización por omisión (ejemplo: ``float.init`` -> *NaN*
794 .. [#dnan] Del inglés *Not A Number*, es un valor especial codificado según
795 IEEE 754-2008 [IEEE754]_ que indica que estamos ante un valor inválido.
798 Representación textual del símbolo o expresión (ejemplo:
799 ``(1+2).stringof`` -> ``"1 + 2"``).
802 Representación textual del tipo *mutilado* [#dmangle]_ [DWAB]_.
804 .. [#dmangle] *Name mangling* es el nombre dado comúnmente a una técnica
805 necesaria para poder sobrecargar nombres de símbolos. Consiste en
806 codificar los nombres de las funciones tomando como entrada el nombre de
807 la función y la cantidad y tipo de parámetros, asegurando que dos
808 funciones con el mismo nombre pero distintos parámetros (sobrecargada)
809 tengan nombres distintos.
812 Alineación de una estructura o tipo.
814 Estos son solo los *properties* predefinidos para todos los tipos, pero hay
815 una cantidad considerable de *properties* extra para cada tipo.
821 Programación confiable
822 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
824 Programación confiable se refiere a las capacidades o facilidades que provee
825 el lenguaje para evitar fallas de manera temprana (o la capacidad de evitar
826 que ciertas fallas puedan existir directamente). D_ presta particular atención
827 a esto y provee las siguientes herramientas:
830 D_ soporta excepciones de manera similar a Java_: provee ``try``, ``catch``
831 y ``finally``. Esto permite que los errores difícilmente pasen
832 silenciosamente sin ser detectados [DWEX]_.
835 Es una condición que debe cumplirse siempre en un programa, como un chequeo
836 de integridad. Esto es muy utilizado en C/C++, donde ``assert()`` es una
837 *macro* que solo se compila cuando la *macro* ``NDEBUG`` no está definida.
838 Esto permite eliminar los chequeos de integridad del programa, que pueden
839 ser costosos, para versiones que se suponen estables.
841 D_ lleva este concepto más allá y hace al ``assert`` parte del lenguaje
842 [DWCP]_. Si una verificación no se cumple, lanza una excepción. El
843 ``assert`` no es compilado cuando se utiliza una opción del compilador.
847 File f = open("archivo");
851 El diseño por contrato es un concepto introducido por el lenguaje Eiffel_
852 a mediados/finales de los '80. Se trata de incorporar en el lenguaje las
853 herramientas para poder aplicar verificaciones formales a las interfaces de
856 D_ implementa las siguientes formas de diseño por contrato (todas se
857 ejecutan siempre y cuando no se compile en modo *release*, de manera de no
858 sacrificar rendimiento cuando es necesario) [DWCP]_:
860 Pre y post condiciones
863 double raiz_cuadrada(double x)
864 in { // pre-condiciones
867 out (resultado) { // post-condiciones
868 assert (resultado >= 0.0);
870 assert (resultado < x);
872 assert (resultado > x);
874 assert (resultado == 1);
880 Invariantes de representación
881 La invariante de representación es un método de una clase o estructura
882 que es verificada cuando se completa su construcción, antes de la
883 destrucción, antes y después de ejecutar cualquier función miembro
884 pública y cuando se lo requiere de forma explícita utilizando
893 assert(1 <= dia && dia <= 31);
894 assert(0 <= hora && hora < 24);
899 Es posible incluir pequeñas pruebas unitarias en el lenguaje. Éstas son
900 ejecutadas (cuando no se compila en modo *release*) al comenzar el
901 programa, antes de que la función ``main()`` [DWUT]_.
908 assert (fecha.dia == 5);
912 Orden de construcción estática
913 A diferencia de C++, D_ garantiza el orden de inicialización de los
914 módulos. Si bien en C++ no hay módulos si no unidades de compilación, es
915 posible que se ejecute código antes del ``main()`` en C++, si hay, por
916 ejemplo, instancias globales con un constructor definido. C++ no garantiza
917 un orden de inicialización, lo que trae muchos problemas. En D_ se define
918 el orden de inicialización y es el mismo orden en que el usuario importa
921 Inicialización garantizada
922 Todas las variables son inicializadas por el lenguaje (a menos que el
923 usuario pida explícitamente que no lo sean) [DWTY]_ [DWVI]_. Siempre que
924 sea posible se elijen valores de inicialización que permitan saber al
925 programador que la variable no fue inicializada explícitamente, de manera
926 de poder detectar errores de manera temprana.
930 double d; // inicializado a NaN
931 int x; // inicializado a 0
932 Fecha f; // inicializado a null
933 byte[5] a; // inicializados todos los valores a 0
934 long l = void; // NO inicializado (explícitamente)
936 *RAII* (*Resource Adquisition Is Initialization*)
937 Es una técnica muy utilizada en C++ que consiste en reservar recursos por
938 medio de la construcción de un objeto y liberarlos cuando se libera éste.
939 Al llamarse al destructor de manera automática cuando se sale del *scope*,
940 se asegura que el recurso será liberado también.
942 Esta técnica es la base para desarrollar código seguro en cuanto
943 a excepciones (*exception-safe*) [SUTT99]_.
945 En D_ no es tan común utilizar *RAII* dada la existencia del recolector de
946 basura (en la mayoría de los casos el recurso a administrar es
947 sencillamente memoria). Sin embargo en los casos en donde es necesario,
948 puede utilizarse *RAII* mediante la utilización de la palabra reservada
949 ``scope``, que limita la vida de un objeto a un bloque de código [DWES]_.
954 this() { /* adquiere recurso */ }
955 ~this() { /* libera recurso */ }
958 scope Archivo archivo = new Archivo;
960 } // en este punto se llama al destructor de archivo
962 Guardias de bloque (*scope guards*)
963 Además de poder limitar la vida de una instancia a un *scope*, es posible
964 especificar un bloque de código arbitrario a ejecutar al abandonar un
965 *scope*, ya sea cuando se sale del *scope* normalmente o por una falla
973 lock.unlock(); // ejecutado siempre que salga de f()
974 auto trans = new Transaccion;
976 trans.commit(); // ejecutado si sale con "return"
978 trans.rollback(); // ejecutado si sale por una excepción
980 throw Exception("error"); // lock.unlock() y trans.rollback()
981 else if (otra_condicion)
982 return 5; // lock.unlock() y trans.commit()
983 return 0; // lock.unlock() y trans.commit()
986 Esta es una nueva forma de poder escribir código *exception-safe*, aunque
987 el programador debe tener un poco más de cuidado de especificar las
988 acciones a ejecutar al finalizar el *scope*.
990 Primitivas de sincronización de hilos
991 La programación multi-hilo está directamente soportada por el lenguaje,
992 y se provee una primitiva de sincronización al igual que Java_. La palabra
993 reservada ``synchronized`` puede aparecer como modificador de métodos (en
994 cuyo caso se utiliza un *lock* por clase para sincronizar) o como una
995 sentencia, en cuyo caso se crea un *lock* global por cada bloque
996 ``synchronized`` a menos que se especifique sobre qué objeto realizar la
997 sincronización [DWSY]_. Por ejemplo::
1000 synchronized void bar() { /* cuerpo */ }
1007 synchronized (this) { /* cuerpo */ }
1013 ----------------------------------------------------------------------------
1015 Hay, hasta el momento, 3 compiladores de D_ de buena calidad: DMD_, GDC_
1018 DMD_ es el compilador de referencia, escrito por `Walter Bright`_. El
1019 *front-end* [#frontend]_ de este compilador ha sido liberado bajo licencia
1020 Artistic_/GPL_ y es utilizado por los otros dos compiladores, por lo tanto en
1021 realidad hay solo un compilador disponible con 3 *back-ends* [#backend]_
1024 .. [#frontend] *Front-end* es la parte del compilador encargada de hacer el
1025 análisis léxico, sintáctico y semántico del código fuente, generando una
1026 representación intermedia que luego el *back-end* convierte a código de
1029 .. [#backend] El *back-end* es la parte del compilador encargada de convertir
1030 la representación intermedia generada por el *front-end* a código de
1033 Con `DMD 1.041`__ se publicó el código fuente completo del compilador, pero
1034 con una licencia muy restrictiva para uso personal, por lo que el único efecto
1035 logrado por esto es que la gente pueda mandar parches o correcciones del
1036 compilador pero no lo convierte en `Software Libre`_, siendo el único de los
1037 3 compiladores que no tiene esta característica.
1039 __ http://www.digitalmars.com/d/1.0/changelog.html#new1_041
1041 El compilador GDC_ es el *front-end* de DMD_ utilizando al compilador GCC_
1042 como *back-end*. Fue un muy buen compilador pero estuvo abandonado por casi
1043 tres años. A mediados de este año recibió un nuevo impulso y de a poco se está
1044 poniendo al día con los *front-ends* actuales de DMD_ 1.0 y 2.0, aunque la
1045 versión 2.0 viene bastante más rezagada y todavía no es una alternativa viable
1048 LDC_ sufrió una suerte similar, es un compilador joven que utiliza como
1049 *back-end* a LLVM_ (una infraestructura modera para construir compiladores),
1050 nacido a mediados de 2007 como un proyecto personal y privado de Tomas
1051 Lindquist Olsen, que estuvo trabajando de forma privada en el proyecto hasta
1052 mediados de 2008, momento en que decide publicar el código mediante una
1053 licencia libre. Para ese entonces el compilador era todavía inestable
1054 y faltaban implementar varias cosas, pero el estado era lo suficientemente
1055 bueno como para captar varios colaboradores muy capaces, como `Christian
1056 Kamm`_ y Frits Van Bommel que rápidamente se convirtieron en parte fundamental
1057 del proyecto. El primer *release* (0.9) de una versión relativamente completa
1058 y estable fue a principios de 2009 que fue seguido por la versión 0.9.1 que
1059 como puntos más salientes agregó soporte para x86-64 y assembly embebido. El
1060 compilador tuvo un crecimiento excepcional pero estuvo muy inactivo por algún
1061 tiempo y, si bien sigue siendo mantenido, en general los nuevos *front-end* de
1062 DMD_ llevan tiempo de integrar y no está al día con el *back-end* de LLVM_
1063 (por ejemplo desde que se actualizó para utilizar LLVM_ 2.7 que perdió la
1064 capacidad de generar símbolos de depuración).
1066 Además de estos compiladores hay varios otros experimentales, pero ninguno de
1067 ellos de calidad suficiente todavía. Por ejemplo hay un compilador
1068 experimental que emite *CIL* (*Common Intermediate Language*), el *bytecode*
1069 de `.NET`_, llamado DNet_. También hay un *front-end* escrito en D_, llamado
1072 Originalmente, dado que GDC_ estaba siendo mantenido y que LDC_ no existía,
1073 este trabajo iba a ser realizado utilizando GDC_ como compilador, dado que al
1074 ser `Software Libre`_ podía ser modificado de ser necesario. Pero finalmente,
1075 dada la poca confiabilidad que presenta la continuidad del desarrollo de tanto
1076 GDC_ como LDC_, y que el código de DMD_ está disponible en su totalidad
1077 (aunque no sea `Software Libre`_ por completo), se optó por utilizar este
1078 último, dado que es la implementación de referencia que fue más constantemente
1079 mantenida y desarrollada.
1082 .. include:: links.rst
1084 .. vim: set ts=3 sts=3 sw=3 et tw=78 spelllang=es :