El origen del lenguaje está plasmado en su sitio web, en donde se cita:
- It seems to me that most of the "new" programming languages fall
- into one of two categories: Those from academia with radical new
- paradigms and those from large corporations with a focus on RAD and
- the web. Maybe it's time for a new language born out of practical
- experience implementing compilers.
+ It seems to me that most of the "new" programming languages fall into
+ one of two categories: Those from academia with radical new paradigms
+ and those from large corporations with a focus on RAD and the web. Maybe
+ it's time for a new language born out of practical experience
+ implementing compilers.
Esto podría traducirse como:
- Parece que la mayoría de los lenguajes de programación "nuevos" caen
- en 2 categorías: aquellos académicos con nuevos paradigmas radicales y
- aquellos de grandes corporaciones con el foco en el desarrollo rápido
- y web. Tal vez es hora de que nazca un nuevo lenguaje de la experiencia
- práctica implementando compiladores.
+ Parece que la mayoría de los lenguajes de programación "nuevos" caen en
+ 2 categorías: aquellos académicos con nuevos paradigmas radicales
+ y aquellos de grandes corporaciones con el foco en el desarrollo rápido
+ y web. Tal vez es hora de que nazca un nuevo lenguaje de la experiencia
+ práctica implementando compiladores.
La versión 1.0 fue más bien una etiqueta arbitraria que un indicador real
de estar ante una versión estable y completa. Luego de liberarse se
inmutabilidad y funciones *puras* [#dpure]_ (a mediados de 2007).
.. [#dpure] Por funciones *puras* en D_ se entiende que no tienen efectos
- colaterales. Es decir, una función pura siempre que se llame
- con la misma entrada producirá el mismo resultado. Esto es
- análogo a como funcionan los lenguajes funcionales en general,
- abríendo la puerta a la programación de estilo funcional en
- D_.
+ colaterales. Es decir, una función pura siempre que se llame con la
+ misma entrada producirá el mismo resultado. Esto es análogo a como
+ funcionan los lenguajes funcionales en general, abríendo la puerta a la
+ programación de estilo funcional en
+ D_.
A partir de este momento la versión 1.0 quedó *teóricamente* congelada,
introduciendo solo cambios que arreglen errores (*bug fixes*),
En este contexto nacen primero Mango_ y luego Ares_. Mango_ fue creada por
Kris Macleod Bell a principios de 2004 como una biblioteca que provee
servicios básicos de entrada/salida (o *I/O* de *input/output* en inglés)
-de alto rendimiento. Siendo estos servicios algo básico lo más natural
+de alto rendimiento. Siendo estos servicios algo básico lo más natural
hubiera sido que se encuentren en la biblioteca estándar de D_ pero por las
dificultades para contribuir a ésta, se desarrolla como una biblioteca
-separada. A mediados de 2004 Sean Kelly crea Ares_ , con las mismas
+separada. A mediados de 2004 Sean Kelly crea Ares_ , con las mismas
motivaciones pero con la intención de crear una biblioteca base (conocida
en inglés como *runtime*) que incluye los servicios básicos que necesita el
lenguaje (información de tipos, manejo de excepciones e hilos, creación
D_ es un lenguaje de programación con sintaxis tipo C, multi-paradigma,
compilado, con *tipado* fuerte y estático, buenas capacidades tanto de
-programación de bajo nivel (*system programming*) como de alto nivel. Es
+programación de bajo nivel (*system programming*) como de alto nivel. Es
compatible de forma binaria con C (se puede enlazar código objeto C con
código objeto D). Con estas características, D_ logra llenar un vacío
importante que hay entre lo lenguajes de alto bajo nivel y los de alto
agrupadas por unidades funcional o paradigmas que soporta:
+
.. _ref_d_generic:
Programación genérica y meta-programación
y meta-programación:
``if`` estático (``static if``):
- puede verse como similar a la directiva del preprocesador de C/C++
- ``#if``, pero a diferencia de esto, en D_ el ``static if`` tiene acceso
- a todos los símbolos del compilador (constantes, tipos, variables, etc).
+ puede verse como similar a la directiva del preprocesador de C/C++
+ ``#if``, pero a diferencia de esto, en D_ el ``static if`` tiene acceso
+ a todos los símbolos del compilador (constantes, tipos, variables, etc).
- Ejemplo::
+ Ejemplo::
- static if ((void*).sizeof == 4)
- pragma(msg, "32 bits");
+ static if ((void*).sizeof == 4)
+ pragma(msg, "32 bits");
- Más información en http://www.digitalmars.com/d/1.0/version.html#staticif
+ Más información en http://www.digitalmars.com/d/1.0/version.html#staticif
Inferencia de tipos básica implícita y explícita (mediante ``typeof``):
- si no se especifica un tipo al declarar una variable, se infiere del tipo
- de su inicializador.
+ si no se especifica un tipo al declarar una variable, se infiere del
+ tipo de su inicializador.
- Ejemplo::
+ Ejemplo::
- static i = 5; // i es int
- const d = 6.0; // d es double
- auto s = "hola"; // s es string (que es un alias de char[])
+ static i = 5; // i es int
+ const d = 6.0; // d es double
+ auto s = "hola"; // s es string (que es un alias de char[])
- Más información en
- http://www.digitalmars.com/d/1.0/declaration.html#AutoDeclaration
+ Más información en
+ http://www.digitalmars.com/d/1.0/declaration.html#AutoDeclaration
- Mediante el uso de ``typeof`` se puede solicitar el tipo de una expresión
- arbitraria.
+ Mediante el uso de ``typeof`` se puede solicitar el tipo de una
+ expresión arbitraria.
- Ejemplo::
+ Ejemplo::
- typeof(5 + 6.0) d; // d es double
+ typeof(5 + 6.0) d; // d es double
- Más información en http://www.digitalmars.com/d/1.0/declaration.html#typeof
+ Más información en http://www.digitalmars.com/d/1.0/declaration.html#typeof
Iteración sobre colecciones (``foreach``):
- cualquier tipo de colección (arreglos estáticos y dinámicos, arreglos
- asociativos, clases, estructuras o delegados) puede ser iterada mediante
- la sentencia ``foreach``.
+ cualquier tipo de colección (arreglos estáticos y dinámicos, arreglos
+ asociativos, clases, estructuras o delegados) puede ser iterada mediante
+ la sentencia ``foreach``.
- Ejemplo::
+ Ejemplo::
- int[] a = [ 1, 2, 3 ];
- int total = 0;
- foreach (i; a)
- total += i;
+ int[] a = [ 1, 2, 3 ];
+ int total = 0;
+ foreach (i; a)
+ total += i;
*Templates*:
- clases y funciones pueden ser parametrizadas. Esto permite desarrollar
- algoritmos genéricos sin importar el tipo de los datos de entrada,
- siempre y cuando todos los tipos tengan una *interfaz* común. Esto
- también es conocido como *polimorfismo en tiempo de compilación*, y es la
- forma más básica de programación genérica.
+ clases y funciones pueden ser parametrizadas. Esto permite desarrollar
+ algoritmos genéricos sin importar el tipo de los datos de entrada,
+ siempre y cuando todos los tipos tengan una *interfaz* común. Esto
+ también es conocido como *polimorfismo en tiempo de compilación*, y es
+ la forma más básica de programación genérica.
+
+ Ejemplo::
+
+ T sumar(T)(T x, T y) { return x + y; }
+ auto i = sumar!(int)(5, 6); // i == 11
+ auto f = sumar!(float)(5, 6); // j == 11.0f
+
+ Además se pueden definir bloques de declaraciones parametrizados (esto
+ no es posible en C++), permitiendo instanciar dicho bloque con
+ parámetros particulares. Esto sirve como un mecanismo para la
+ reutilización de código, ya que puede incluirse un mismo bloque en
+ distintos lugares (por ejemplo clases). Un bloque parametrizado puede
+ verse como una especie de módulo.
+
+ Ejemplo::
+
+ template bloque(T, U) {
+ T x;
+ U foo(T y);
+ }
- Ejemplo::
+ bloque!(int, float).x = 5;
+ float f = bloque!(int, float).foo(7);
- T sumar(T)(T x, T y) { return x + y; }
- auto i = sumar!(int)(5, 6); // i == 11
- auto f = sumar!(float)(5, 6); // j == 11.0f
+ La utilidad más prominente de los bloques parametrizados se da al
+ acompañarse de *mixins*.
- Además se pueden definir bloques de declaraciones parametrizados (esto no
- es posible en C++), permitiendo instanciar dicho bloque con parámetros
- particulares. Esto sirve como un mecanismo para la reutilización de
- código, ya que puede incluirse un mismo bloque en distintos lugares (por
- ejemplo clases). Un bloque parametrizado puede verse como una especie de
- módulo.
+Instanciación implícita de funciones parametrizadas:
+ el lenguaje es capaz de deducir los parámetros siempre que no hayan
+ ambigüedades
- Ejemplo::
+ Ejemplo::
- template bloque(T, U) {
- T x;
- U foo(T y);
- }
+ auto i = sumar(5, 6); // i == 11
+ auto f = sumar(5.0f, 6.0f); // f == 11.0f
- bloque!(int, float).x = 5;
- float f = bloque!(int, float).foo(7);
+Especialización explícita y parcial de *templates*:
+ la especialización de *templates* consiste, al igual que en C++, en
+ proveer una implementación especializada para un tipo de dato (o valor)
+ de los parámetros. Especialización parcial se refiere a la capacidad
+ de especializar un parámetro a través de un subtipo. Por ejemplo, se
+ puede especializar un *template* para cualquier tipo de puntero, o para
+ cualquier tipo de arreglo dinámico, sin necesidad de especificar el tipo
+ al que apunta dicho puntero o el tipo almacenado por el arreglo.
- La utilidad más prominente de los bloques parametrizados se da al
- acompañarse de *mixins*.
+ Ejemplo de especialización::
-Instanciación implícita de funciones parametrizadas:
- el lenguaje es capaz de deducir los parámetros siempre que no hayan
- ambigüedades
+ T sumar(T: int)(T x, T y) { return x + y + 1; }
+ auto i = sumar(5, 6); // i == 12
+ auto f = sumar(5.0f, 6.0f) // f == 11.0f
- Ejemplo::
+ Ejemplo de especialización parcial::
- auto i = sumar(5, 6); // i == 11
- auto f = sumar(5.0f, 6.0f); // f == 11.0f
+ T sumar(T: T*)(T x, T y) { return *x + *y; }
+ int x = 5, y = 6;
+ auto i = sumar(&x, &y); // i == 11
+ float v = 5.0f, w = 6.0f;
+ auto f = sumar(&v, &w); // f == 11.0f
-Especialización explícita y parcial de *templates*:
- la especialización de *templates* consiste, al igual que en C++, en
- proveer una implementación especializada para un tipo de dato (o valor)
- de los parámetros. Especialización parcial se refiere a la capacidad de
- especializar un parámetro a través de un subtipo. Por ejemplo, se puede
- especializar un *template* para cualquier tipo de puntero, o para
- cualquier tipo de arreglo dinámico, sin necesidad de especificar el tipo
- al que apunta dicho puntero o el tipo almacenado por el arreglo.
+Tipos, valores (incluyendo *strings*) y *templates* como parámetros:
+ esto es otro bloque de construcción importantísimo para la programación
+ genérica en D, ya que combinando *templates* que toman *strings* como
+ parámetro en combinación con *string mixins* pueden hacerse toda clase
+ de meta-programas.
+
+ Ejemplo::
+
+ template hash(string s, uint so_far=0) {
+ static if (s.length == 0)
+ const hash = sofar;
+ else
+ const hash = hash!(s[1 .. length], sofar * 11 + s[0]);
+ }
+ string s = hash!("hola"); // calculado en tiempo de compilación
- Ejemplo de especialización::
+Cantidad de parámetros variables para *templates*:
+ Esto permite implementar tuplas u otros algoritmos que inherentemente
+ deben tomar parámetros variables en tiempo de compilación.
- T sumar(T: int)(T x, T y) { return x + y + 1; }
- auto i = sumar(5, 6); // i == 12
- auto f = sumar(5.0f, 6.0f) // f == 11.0f
+ Ejemplo::
- Ejemplo de especialización parcial::
+ double sumar(T...)(T t) {
+ double res = 0.0;
+ foreach (x; t)
+ res += x;
+ return res;
+ }
+ double d = sumar(1, 2.0, 3.0f, 4l); // d == 10.0
- T sumar(T: T*)(T x, T y) { return *x + *y; }
- int x = 5, y = 6;
- auto i = sumar(&x, &y); // i == 11
- float v = 5.0f, w = 6.0f;
- auto f = sumar(&v, &w); // f == 11.0f
+*CTFE* (*compile-time function execution*):
+ si una función cumple ciertas reglas básicas (como por ejemplo no tener
+ efectos colaterales) puede ser ejecutada en tiempo de compilación en vez
+ de tiempo de ejecución. Esto permite hacer algunos cálculos que no
+ cambian de ejecución en ejecución al momento de compilar, mejorando la
+ performance o permitiendo formas avanzadas de metaprogramación. Esta
+ característica se vuelve particularmente útil al combinarse con *string
+ mixins*.
+
+ Ejemplo::
+
+ int factorial(int n) {
+ if (n == 1)
+ return 1;
+ else
+ return n * factorial(n - 1);
+ }
+ static int x = factorial(5); // calculado en tiempo de compilación
+ int x = factorial(5); // calculado en tiempo de ejecución
-Tipos, valores (incluyendo *strings*) y *templates* como parámetros:
- esto es otro bloque de construcción importantísimo para la programación
- genérica en D, ya que combinando *templates* que toman *strings* como
- parámetro en combinación con *string mixins* pueden hacerse toda clase de
- meta-programas.
+ Esta característica es vital para evitar la duplicación de código.
- Ejemplo::
+*Mixins*, incluyendo *string mixins*:
+ la palabra *mixin* tiene significados distintos en varios lenguajes de
+ programación. En D_ *mixin* significa tomar una secuencia arbitraria de
+ declaraciones e insertarla en el contexto (*scope*) actual. Esto puede
+ realizarse a nivel global, en clases, estructuras o funciones. Esto
+ sirve como un mecanismo para evitar duplicación de código que puede ser
+ introducida por la falta de herencia múltiple.
- template hash(string s, uint so_far=0) {
- static if (s.length == 0)
- const hash = sofar;
- else
- const hash = hash!(s[1 .. length], sofar * 11 + s[0]);
- }
- string s = hash!("hola"); // calculado en tiempo de compilación
+ Ejemplo::
-Cantidad de parámetros variables para *templates*:
- Esto permite implementar tuplas u otros algoritmos que inherentemente
- deben tomar parámetros variables en tiempo de compilación.
+ class A {
+ mixin bloque!(int, float);
+ }
+ A a = new A;
+ a.x = 5;
+ float f = a.foo(a.x);
- Ejemplo::
+ class B {
+ mixin bloque!(long, double);
+ }
+ B b = new B;
+ b.x = 5l;
+ double d = a.foo(a.x);
- double sumar(T...)(T t) {
- double res = 0.0;
- foreach (x; t)
- res += x;
- return res;
- }
- double d = sumar(1, 2.0, 3.0f, 4l); // d == 10.0
+ *String mixin* se refiere a la capacidad de *incrustar* un *string* que
+ contenga un fragmento de código en un programa como si este fragmento
+ hubiera sido escrito en el código fuente directamente por el
+ programador. Esto permite hacer manipulaciones arbitrariamente
+ complejas en combinación con funciones ejecutadas en tiempo de
+ compilación.
-*CTFE* (*compile-time function execution*):
- si una función cumple ciertas reglas básicas (como por ejemplo no tener
- efectos colaterales) puede ser ejecutada en tiempo de compilación en vez
- de tiempo de ejecución. Esto permite hacer algunos cálculos que no
- cambian de ejecución en ejecución al momento de compilar, mejorando la
- performance o permitiendo formas avanzadas de metaprogramación. Esta
- característica se vuelve particularmente útil al combinarse con *string
- mixins*.
-
- Ejemplo::
-
- int factorial(int n) {
- if (n == 1)
- return 1;
- else
- return n * factorial(n - 1);
- }
- static int x = factorial(5); // calculado en tiempo de compilación
- int x = factorial(5); // calculado en tiempo de ejecución
-
- Esta característica es vital para evitar la duplicación de código.
+ Ejemplo::
-*Mixins*, incluyendo *string mixins*:
- la palabra *mixin* tiene significados distintos en varios lenguajes de
- programación. En D_ *mixin* significa tomar una secuencia arbitraria de
- declaraciones e insertarla en el contexto (*scope*) actual. Esto puede
- realizarse a nivel global, en clases, estructuras o funciones. Esto sirve
- como un mecanismo para evitar duplicación de código que puede ser
- introducida por la falta de herencia múltiple.
-
- Ejemplo::
-
- class A {
- mixin bloque!(int, float);
- }
- A a = new A;
- a.x = 5;
- float f = a.foo(a.x);
-
- class B {
- mixin bloque!(long, double);
- }
- B b = new B;
- b.x = 5l;
- double d = a.foo(a.x);
-
- *String mixin* se refiere a la capacidad de *incrustar* un *string* que
- contenga un fragmento de código en un programa como si este fragmento
- hubiera sido escrito en el código fuente directamente por el programador.
- Esto permite hacer manipulaciones arbitrariamente complejas en
- combinación con funciones ejecutadas en tiempo de compilación.
-
- Ejemplo::
-
- string generar_sumar(string var_x, string var_y) {
- return "return " ~ var_x ~ " + " ~ var_y ~ ";";
- }
-
- int sumar(int a, int b) {
- mixin(generar_sumar!("a", b"));
- }
-
- Más información en http://www.digitalmars.com/d/1.0/mixin.html
+ string generar_sumar(string var_x, string var_y) {
+ return "return " ~ var_x ~ " + " ~ var_y ~ ";";
+ }
-Expresiones ``is``:
- las *expresiones ``is``* permiten la compilación condicional basada en
- las características de un tipo. Esto se realiza en favor a una técnica
- utilizada en C++ de realizar *pattern matching* sobre los parámetros de
- las plantillas.
+ int sumar(int a, int b) {
+ mixin(generar_sumar!("a", b"));
+ }
- Ejemplo::
+ Más información en http://www.digitalmars.com/d/1.0/mixin.html
- T foo(T)(T x) {
- static if (is(T == class))
- return new T;
- else
- return T.init;
- }
+Expresiones ``is``:
+ las *expresiones ``is``* permiten la compilación condicional basada en
+ las características de un tipo. Esto se realiza en favor a una técnica
+ utilizada en C++ de realizar *pattern matching* sobre los parámetros de
+ las plantillas.
+
+ Ejemplo::
+
+ T foo(T)(T x) {
+ static if (is(T == class))
+ return new T;
+ else
+ return T.init;
+ }
- Esto provee además una forma simple de reflexión en tiempo de
- compilación.
+ Esto provee además una forma simple de reflexión en tiempo de
+ compilación.
- Más información en
- http://www.digitalmars.com/d/1.0/expression.html#IsExpression
+ Más información en
+ http://www.digitalmars.com/d/1.0/expression.html#IsExpression
D_ presenta muchas características de bajo nivel:
Compila a código de máquina nativo:
- no es interpretado ni necesita una máquina virtual como otros lenguajes
- de más alto nivel como Java_, `C#`_, Python_, etc.
+ no es interpretado ni necesita una máquina virtual como otros lenguajes
+ de más alto nivel como Java_, `C#`_, Python_, etc.
Provee acceso a *assembly*:
- por lo tanto, acceso directo al *hardware* y la posibilidad de utilizar
- cualquier característica de éste que no esté disponible en el lenguaje.
+ por lo tanto, acceso directo al *hardware* y la posibilidad de utilizar
+ cualquier característica de éste que no esté disponible en el lenguaje.
- Una ventaja sobre C y C++ es que el lenguaje *assembly* utilizado dentro
- de D_ está especificado, por lo que se puede mantener la portabilidad
- entre compiladores incluso cuando se utiliza *assembly* (mientras que no
- se cambie de arquitectura, por supuesto).
+ Una ventaja sobre C y C++ es que el lenguaje *assembly* utilizado dentro
+ de D_ está especificado, por lo que se puede mantener la portabilidad
+ entre compiladores incluso cuando se utiliza *assembly* (mientras que no
+ se cambie de arquitectura, por supuesto).
``goto``:
- al igual que C y C++, D_ provee la flexibilidad del uso de ``goto``.
+ al igual que C y C++, D_ provee la flexibilidad del uso de ``goto``.
-Compatibilidad con C
- soporta todos los tipos de C y es ABI [#abi]_ compatible con éste. Esto
- permite enlazar archivos objeto estándar de C y D_ en un mismo programa.
- Además permite interoperar con C a través de ``extern (C)``.
+Compatibilidad con C:
+ soporta todos los tipos de C y es ABI [#abi]_ compatible con éste. Esto
+ permite enlazar archivos objeto estándar de C y D_ en un mismo programa.
+ Además permite interoperar con C a través de ``extern (C)``.
- Ejemplo::
+ .. [#abi] Interfaz de Aplicación Binaria (del inglés *Application Binary
+ Interface*).
- extern (C) printf(const char* format, ...);
- printf("3 + 5 == %d\n", 3 + 5); // llama al printf de C
+ Ejemplo::
+
+ extern (C) printf(const char* format, ...);
+ printf("3 + 5 == %d\n", 3 + 5); // llama al printf de C
Manejo de memoria explícito:
- permite alocar estructuras en el *stack* o en el *heap*, haciendo uso de
- los servicios del sistema operativo o la biblioteca estándar de C.
+ permite alocar estructuras en el *stack* o en el *heap*, haciendo uso de
+ los servicios del sistema operativo o la biblioteca estándar de C.
Objetos y arreglos *livianos*:
- por objetos *livianos* se entiende no-polimórficos. Es decir, un
- agrupamiento de variables análogo al ``struct`` de C, sin tabla
- virtual ni otro tipo de *overhead*. Los arreglos *livianos* son
- arreglos estáticos como en C, cuyo tamaño es fijo, también sin ningún
- tipo de *overhead* como C. Además puede alocarse un arreglo dinámicamente
- usando ``malloc()`` y utilizar el operador ``[]`` para accederlo.
-
- Esto también permite interoperar con C, ya que pueden definirse
- ``structs`` y arreglos que pueden ser intercambiados con dicho lenguaje
- sin problemas.
-
- Ejemplo::
-
- struct timeval {
- time_t tv_sec;
- suseconds_t tv_usec;
- }
- extern (C) {
- void* malloc(size_t);
- size_t strlen(const char *);
- int gettimeofday(timeval *, void *);
- }
- char* s = cast(char*) malloc(2);
- s[0] = 'C';
- s[1] = '\0';
- size_t l = strlen(s); // l == 1
- timeval tv;
- gettimeofday(&tv, null);
+ por objetos *livianos* se entiende no-polimórficos. Es decir, un
+ agrupamiento de variables análogo al ``struct`` de C, sin tabla virtual
+ ni otro tipo de *overhead*. Los arreglos *livianos* son arreglos
+ estáticos como en C, cuyo tamaño es fijo, también sin ningún tipo de
+ *overhead* como C. Además puede alocarse un arreglo dinámicamente usando
+ ``malloc()`` y utilizar el operador ``[]`` para accederlo.
+
+ Esto también permite interoperar con C, ya que pueden definirse
+ ``structs`` y arreglos que pueden ser intercambiados con dicho lenguaje
+ sin problemas.
+
+ Ejemplo::
+
+ struct timeval {
+ time_t tv_sec;
+ suseconds_t tv_usec;
+ }
+ extern (C) {
+ void* malloc(size_t);
+ size_t strlen(const char *);
+ int gettimeofday(timeval *, void *);
+ }
+ char* s = cast(char*) malloc(2);
+ s[0] = 'C';
+ s[1] = '\0';
+ size_t l = strlen(s); // l == 1
+ timeval tv;
+ gettimeofday(&tv, null);
Rendimiento:
- la :ref:`ref_d_generic` permite realizar muchas optimizaciones ya que
- se resuelve en tiempo de compilación y por lo tanto aumentando la
- *performance* en la ejecución.
+ la :ref:`ref_d_generic` permite realizar muchas optimizaciones ya que se
+ resuelve en tiempo de compilación y por lo tanto aumentando la
+ *performance* en la ejecución.
Número de punto flotante de 80 bits:
- El tipo ``real`` de D_ tiene precisión de 80 bits si la plataforma lo
- soporta (por ejemplo en i386).
+ El tipo ``real`` de D_ tiene precisión de 80 bits si la plataforma lo
+ soporta (por ejemplo en i386).
Control de alineación de miembros de una estructura:
- Mediante ``align`` se puede especificar la alineación a tener en una
- estructura.
+ Mediante ``align`` se puede especificar la alineación a tener en una
+ estructura.
- Ejemplo::
-
- align (1)
- struct paquete_de_red {
- char tipo;
- short valor;
- }
- // paquete_de_red.sizeof == 3
+ Ejemplo::
+ align (1)
+ struct paquete_de_red {
+ char tipo;
+ short valor;
+ }
+ // paquete_de_red.sizeof == 3
-.. [#abi] Interfaz de Aplicación Binaria (del inglés *Application Binary
- Interface*).
.. _ref_d_high_level:
lenguajes de alto nivel, como Java_ y Python_, por ejemplo:
Manejo automático de memoria:
- al igual que C/C++ y prácticamente cualquier lenguaje imperativo maneja
- automáticamente el *stack*, pero a diferencia de la mayoría de los
- lenguajes de bajo nivel, D_ permite manejar el *heap* de manera
- automática también a través de un *recolección de basura*.
+ al igual que C/C++ y prácticamente cualquier lenguaje imperativo maneja
+ automáticamente el *stack*, pero a diferencia de la mayoría de los
+ lenguajes de bajo nivel, D_ permite manejar el *heap* de manera
+ automática también a través de un *recolección de basura*.
Sistema de paquetes y módulos (similar a Java_ o Python_):
- un módulo es una unidad que agrupa clases, funciones y cualquier otra
- construcción de lenguaje. Un paquete es una agrupación de módulos. D_
- asocia un módulo a un archivo fuente (y un archivo objeto cuando éste es
- compilado) y un paquete a un directorio. A diferencia de C/C++ no
- necesita de un preprocesador para incluir declaraciones de otros
- *módulos* (en C/C++ no existe el concepto de módulo, solo de unidades de
- compilación).
+ un módulo es una unidad que agrupa clases, funciones y cualquier otra
+ construcción de lenguaje. Un paquete es una agrupación de módulos. D_
+ asocia un módulo a un archivo fuente (y un archivo objeto cuando éste es
+ compilado) y un paquete a un directorio. A diferencia de C/C++ no
+ necesita de un preprocesador para incluir declaraciones de otros
+ *módulos* (en C/C++ no existe el concepto de módulo, solo de unidades de
+ compilación).
- Ejemplo:
+ Ejemplo:
- ``a.d``::
+ ``a.d``::
- module a;
- void f() {}
+ module a;
+ void f() {}
- ``b.d``::
+ ``b.d``::
- module b;
- void f() {}
+ module b;
+ void f() {}
- ``c.d``::
+ ``c.d``::
- module c;
- import a;
- import b: f;
- a.f();
- b.f();
- f(); // ejecuta b.f()
+ module c;
+ import a;
+ import b: f;
+ a.f();
+ b.f();
+ f(); // ejecuta b.f()
Funciones y delegados:
- las funciones pueden ser sobrecargadas (funciones con el mismo nombre
- pero distinta cantidad o tipo de parámetros), pueden especificarse
- argumentos de entrada, salida o entrada/salida, argumentos por omisión
- o argumentos evaluados de forma perezosa (*lazy*). Además pueden tener
- una cantidad de argumentos variables pero manteniendo información de
- tipos (más seguro que C/C++).
-
- Los *delegados* son punteros a función con un contexto asociado. Este
- contexto puede ser un objeto (en cuyo caso la función es un método) o un
- *stack frame* (en cuyo caso la función es una función anidada).
-
- Además de esto los delegados son ciudadanos de primera clase
- [#1stclasscity]_, disponiendo de forma literal (delegado anónimo), lo que
- permite construcciones de alto nivel muy conveniente. Los argumentos
- evaluados de forma perezosa no son más que un delegado que se ejecuta
- solo cuando es necesario.
-
- Ejemplo::
-
- bool buscar(T[] arreglo, T item, bool delegate(T x, T y) igual) {
- foreach (t, arreglo)
- if (igual(t, elemento))
- return true;
- return false;
- }
- struct Persona {
- string nombre;
- }
- Persona[] personas;
- // llenas personas
- Persona p;
- p.nombre = "Carlos";
- bool encontrado = buscar(personas, p,
- (Persona x, Persona y) {
- return x.nombre == y.nombre;
- }
- );
+ las funciones pueden ser sobrecargadas (funciones con el mismo nombre
+ pero distinta cantidad o tipo de parámetros), pueden especificarse
+ argumentos de entrada, salida o entrada/salida, argumentos por omisión
+ o argumentos evaluados de forma perezosa (*lazy*). Además pueden tener
+ una cantidad de argumentos variables pero manteniendo información de
+ tipos (más seguro que C/C++).
+
+ Los *delegados* son punteros a función con un contexto asociado. Este
+ contexto puede ser un objeto (en cuyo caso la función es un método) o un
+ *stack frame* (en cuyo caso la función es una función anidada).
+
+ Además de esto los delegados son ciudadanos de primera clase
+ [#1stclasscity]_, disponiendo de forma literal (delegado anónimo), lo
+ que permite construcciones de alto nivel muy conveniente. Los argumentos
+ evaluados de forma perezosa no son más que un delegado que se ejecuta
+ solo cuando es necesario.
+
+ .. [#1stclasscity] Por ciudadano de primera clase se entiende que se trata
+ de un tipo soportado por completo por el lenguaje, disponiendo de
+ expresiones literales anónimas, pudiendo ser almacenados en variables,
+ estructuras de datos, teniendo una identidad intrínseca, más allá de un
+ nombre dado, etc. En realidad los arreglos asociativos no pueden ser
+ expresados como literales anónimos pero sí tienen una sintaxis especial
+ soportada directamente por el lenguaje.
+
+ Ejemplo::
+
+ bool buscar(T[] arreglo, T item, bool delegate(T x, T y) igual) {
+ foreach (t, arreglo)
+ if (igual(t, elemento))
+ return true;
+ return false;
+ }
+ struct Persona {
+ string nombre;
+ }
+ Persona[] personas;
+ // llenas personas
+ Persona p;
+ p.nombre = "Carlos";
+ bool encontrado = buscar(personas, p,
+ (Persona x, Persona y) {
+ return x.nombre == y.nombre;
+ }
+ );
Arreglos *dinámicos* y arreglos asociativos:
- los arreglos *dinámicos* son arreglos de longitud variable manejados
- automáticamente por el lenguaje (análogos al ``std::vector`` de C++).
- Soportan concatenación (a través del operador ``~``), rebanado
- o *slicing* (a través del operador ``[x..y]``) y chequeo de límites
- (*bound checking*).
+ los arreglos *dinámicos* son arreglos de longitud variable manejados
+ automáticamente por el lenguaje (análogos al ``std::vector`` de C++).
+ Soportan concatenación (a través del operador ``~``), rebanado
+ o *slicing* (a través del operador ``[x..y]``) y chequeo de límites
+ (*bound checking*).
- Los arreglos asociativos (también conocidos como *hashes* o diccionarios)
- también son provistos por el lenguaje.
+ Los arreglos asociativos (también conocidos como *hashes*
+ o diccionarios) también son provistos por el lenguaje.
- Ambos son ciudadanos de primera clase, disponiendo de forma literal.
+ Ambos son ciudadanos de primera clase, disponiendo de forma literal.
- Ejemplo::
+ Ejemplo::
- int[] primos = [ 2, 3, 5, 7, 11, 13, 17, 19 ];
- primos ~= [ 23, 29 ];
- auto menores_que_10 = primos[0..4]; // [ 2, 3, 5, 7 ]
- int[string] agenda;
- agenda["Pepe"] = 5555_1234;
+ int[] primos = [ 2, 3, 5, 7, 11, 13, 17, 19 ];
+ primos ~= [ 23, 29 ];
+ auto menores_que_10 = primos[0..4]; // [ 2, 3, 5, 7 ]
+ int[string] agenda;
+ agenda["Pepe"] = 5555_1234;
*Strings*:
- al igual que los delegados y arreglos dinámicos y asociativos, los
- *strings* son ciudadanos de primera clase, teniendo forma literal
- y siendo codificados en UTF-8/16/32. Son un caso particular de arreglo
- dinámico y es posible utilizarlos en sentencias ``switch``/``case``.
+ al igual que los delegados y arreglos dinámicos y asociativos, los
+ *strings* son ciudadanos de primera clase, teniendo forma literal
+ y siendo codificados en UTF-8/16/32. Son un caso particular de arreglo
+ dinámico y es posible utilizarlos en sentencias ``switch``/``case``.
- Ejemplo::
+ Ejemplo::
- string s = "árbol";
+ string s = "árbol";
- switch (s) {
- case "árbol":
- s = "tree";
- default:
- s = "";
- }
+ switch (s) {
+ case "árbol":
+ s = "tree";
+ default:
+ s = "";
+ }
``typedef`` y ``alias``:
- el primero define un nuevo tipo basado en otro. A diferencia de C/C++ el
- tipo original no puede ser implícitamente convertido al tipo nuevo
- (excepto valores literales), pero la conversión es válida en el otro
- sentido (similar a los ``enum`` en C++). Por el contrario, ``alias`` es
- análogo al ``typedef`` de C/C++ y simplemente es una forma de referirse
- al mismo tipo con un nombre distinto.
-
- Ejemplo::
-
- typedef int tipo;
- int foo(tipo x) {}
- tipo t = 10;
- int i = 10;
- foo(t);
- foo(i); // error, no compila
- alias tipo un_alias;
- un_alias a = t;
- foo(a);
+ el primero define un nuevo tipo basado en otro. A diferencia de C/C++ el
+ tipo original no puede ser implícitamente convertido al tipo nuevo
+ (excepto valores literales), pero la conversión es válida en el otro
+ sentido (similar a los ``enum`` en C++). Por el contrario, ``alias`` es
+ análogo al ``typedef`` de C/C++ y simplemente es una forma de referirse
+ al mismo tipo con un nombre distinto.
+
+ Ejemplo::
+
+ typedef int tipo;
+ int foo(tipo x) {}
+ tipo t = 10;
+ int i = 10;
+ foo(t);
+ foo(i); // error, no compila
+ alias tipo un_alias;
+ un_alias a = t;
+ foo(a);
Documentación embebida:
- D_ provee un sistema de documentación embebida, análogo a lo que provee
- Java_ o Python_ en menor medida. Hay comentarios especiales del código
- que pueden ser utilizados para documentarlo de forma tal que luego el
- compilador pueda extraer esa información para generar un documento.
+ D_ provee un sistema de documentación embebida, análogo a lo que provee
+ Java_ o Python_ en menor medida. Hay comentarios especiales del código
+ que pueden ser utilizados para documentarlo de forma tal que luego el
+ compilador pueda extraer esa información para generar un documento.
- Más información en http://www.digitalmars.com/d/1.0/ddoc.html
+ Más información en http://www.digitalmars.com/d/1.0/ddoc.html
Números complejos:
- D_ soporta números complejos como ciudadanos de primera clase. Soporta
- forma literal de números imaginarios y complejos.
+ D_ soporta números complejos como ciudadanos de primera clase. Soporta
+ forma literal de números imaginarios y complejos.
- Ejemplo::
+ Ejemplo::
- ifloat im = 5.0i;
- float re = 1.0;
- cfloat c = re + im; // c == 1.0 + 5.0i
-
-.. [#1stclasscity] Por ciudadano de primera clase se entiende que se
- trata de un tipo soportado por completo por el lenguaje, disponiendo de
- expresiones literales anónimas, pudiendo ser almacenados en variables,
- estructuras de datos, teniendo una identidad intrínseca, más allá
- de un nombre dado, etc. En realidad los arreglos asociativos no pueden
- ser expresados como literales anónimos pero sí tienen una sintaxis
- especial soportada directamente por el lenguaje.
+ ifloat im = 5.0i;
+ float re = 1.0;
+ cfloat c = re + im; // c == 1.0 + 5.0i
características más salientes se encuentran:
Objetos *pesados*:
- objetos polimórficos como los de cualquier lenguaje con orientación real
- a objetos. Estos objetos poseen una tabla virtual para *dispatch*
- dinámico, todos los métodos son virtuales a menos que se indique lo
- contrario y tienen semántica de referencia [#drefsem]_. Estos objetos
- tienen un *overhead* comparados a los objetos *livianos* pero aseguran
- una semántica segura para trabajar con orientación a objetos, evitando
- problemas con los que se enfrenta C++ (como *slicing* [#dslicing]_)
- debido a que permite semántica por valor [#dvalsem]_.
-
- D_ además soporta tipos de retorno covariantes para funciones virtuales.
- Esto significa que una función sobreescrita por una clase derivada puede
- retornar un tipo que sea derivado del tipo retornado por la función
- original sobreescrita.
-
- Ejemplo::
-
- class A { }
- class B : A { }
-
- class Foo {
- A test() { return null; }
- }
+ objetos polimórficos como los de cualquier lenguaje con orientación real
+ a objetos. Estos objetos poseen una tabla virtual para *dispatch*
+ dinámico, todos los métodos son virtuales a menos que se indique lo
+ contrario y tienen semántica de referencia [#drefsem]_. Estos objetos
+ tienen un *overhead* comparados a los objetos *livianos* pero aseguran
+ una semántica segura para trabajar con orientación a objetos, evitando
+ problemas con los que se enfrenta C++ (como *slicing* [#dslicing]_)
+ debido a que permite semántica por valor [#dvalsem]_.
+
+ .. [#drefsem] Semántica de referencia significa que el tipo es tratado como
+ si fuera un puntero. Nunca se hacen copias del objeto, siempre se pasa
+ por referencia.
+
+ .. [#dslicing] Este problema se da en C++ cuando se pasa una clase derivada
+ a una función que acepta una clase base por valor como parámetro. Al
+ realizarse una copia de la clase con el constructor de copia de la clase
+ base, se pierden (o *rebanan*) los atributos de la clase derivada, y la
+ información de tipos en tiempo de ejecución (*RTTI*).
+
+ .. [#dvalsem] Semántica de valor significa que el tipo es tratado como si
+ fuera un valor concreto. En general se pasa por valor y se hacen copias
+ a menos que se utilice explícitamente un puntero.
+
+ D_ además soporta tipos de retorno covariantes para funciones virtuales.
+ Esto significa que una función sobreescrita por una clase derivada puede
+ retornar un tipo que sea derivado del tipo retornado por la función
+ original sobreescrita.
+
+ Ejemplo::
+
+ class A { }
+ class B : A { }
+
+ class Foo {
+ A test() { return null; }
+ }
- class Bar : Foo {
- B test() { return null; } // sobreescribe y es covariante con Foo.test()
- }
+ class Bar : Foo {
+ B test() { return null; } // sobreescribe y es covariante con Foo.test()
+ }
- Más información en http://www.digitalmars.com/d/1.0/function.html
+ Más información en http://www.digitalmars.com/d/1.0/function.html
Interfaces:
- D_ no soporta herencia múltiple pero sí interfaces. Una interfaz es
- básicamente una tabla virtual, una definición de métodos virtuales que
- debe proveer una clase. Las interfaces no proveen una implementación de
- dichos métodos, ni pueden tener atributos. Esto simplifica mucho el
- lenguaje y no se pierde flexibilidad porque puede conseguirse el mismo
- efecto de tener herencia múltiple a través de interfaces y *mixins* para
- proveer una implementación o atributos en común a varias clases que
- implementan la misma interfaz.
+ D_ no soporta herencia múltiple pero sí interfaces. Una interfaz es
+ básicamente una tabla virtual, una definición de métodos virtuales que
+ debe proveer una clase. Las interfaces no proveen una implementación de
+ dichos métodos, ni pueden tener atributos. Esto simplifica mucho el
+ lenguaje y no se pierde flexibilidad porque puede conseguirse el mismo
+ efecto de tener herencia múltiple a través de interfaces y *mixins* para
+ proveer una implementación o atributos en común a varias clases que
+ implementan la misma interfaz.
Sobrecarga de operadores:
- la sobrecarga de operadores permite que un objeto tenga una sintaxis
- similar a un tipo de dato nativo. Esto es muy importante además para la
- programación genérica.
+ la sobrecarga de operadores permite que un objeto tenga una sintaxis
+ similar a un tipo de dato nativo. Esto es muy importante además para la
+ programación genérica.
Clases anidadas:
- al igual que C (con respecto a ``struct``) y C++, pueden anidarse clases
- dentro de clases. D_ sin embargo provee la posibilidad de acceder
- a atributos de la instancia exterior desde la anidada.
-
- Ejemplo::
-
- class Exterior {
- int m;
- class Anidada {
- int foo() {
- return m; // ok, puede acceder a un miembro de Exterior
- }
+ al igual que C (con respecto a ``struct``) y C++, pueden anidarse clases
+ dentro de clases. D_ sin embargo provee la posibilidad de acceder
+ a atributos de la instancia exterior desde la anidada.
+
+ Ejemplo::
+
+ class Exterior {
+ int m;
+ class Anidada {
+ int foo() {
+ return m; // ok, puede acceder a un miembro de Exterior
+ }
+ }
}
- }
-
- Esto tiene un pequeño *overhead* ya que la clase ``Anidada`` debe guardar
- un puntero a la clase ``Exterior``. Si no se necesita este comportamiento
- es posible evitar este *overhead* utilizando ``static``, en cuyo caso
- solo puede acceder a atributos estáticos de la clase ``Exterior``.
-
- Ejemplo::
-
- class Exterior {
- int m;
- static int n;
- static class Anidada {
- int foo() {
- //return m; // error, miembro de Exterior
- return n; // ok, miembro estático de Exterior
- }
+
+ Esto tiene un pequeño *overhead* ya que la clase ``Anidada`` debe
+ guardar un puntero a la clase ``Exterior``. Si no se necesita este
+ comportamiento es posible evitar este *overhead* utilizando ``static``,
+ en cuyo caso solo puede acceder a atributos estáticos de la clase
+ ``Exterior``.
+
+ Ejemplo::
+
+ class Exterior {
+ int m;
+ static int n;
+ static class Anidada {
+ int foo() {
+ //return m; // error, miembro de Exterior
+ return n; // ok, miembro estático de Exterior
+ }
+ }
}
- }
Propiedades (*properties*):
- en D_ se refiere a funciones miembro que pueden ser tratadas
- sintácticamente como campos de esa clase/estructura.
-
- Ejemplo::
-
- class Foo {
- int data() { return _data; } // propiedad de lectura
- int data(int value) { return _data = value; } // de escritura
- private int _data;
- }
- Foo f = new Foo;
- f.data = 1; // llama a f.data(1)
- int i = f.data; // llama a f.data()
-
- Además tipos nativos, clases, estructuras y expresiones tienen
- *properties* predefinidos, por ejemplo:
-
- ``sizeof``:
- tamaño ocupado en memoria (ejemplo: ``int.sizeof`` -> 4).
-
- ``init``:
- valor de inicialización por omisión (ejemplo: ``float.init`` -> *NaN*
- [#dnan]_).
-
- ``stringof``:
- representación textual del tipo (ejemplo: ``(1+2).stringof`` -> ``"1
- + 2"``).
-
- ``mangleof``:
- representación textual del tipo *mutilado* [#dmangle]_.
-
- ``alignof``
- alineación de una estructura o tipo.
-
- Estos son solo los *properties* predefinidos para todos los tipos, pero
- hay una cantidad considerable de *properties* extra para cada tipo.
-
- Más información sobre *properties* de clases en
- http://www.digitalmars.com/d/1.0/property.html#classproperties
- y sobre *properties* predefinidos en
- http://www.digitalmars.com/d/1.0/property.html
-
-
-.. [#drefsem] Semántica de referencia significa que el tipo es tratado como
- si fuera un puntero. Nunca se hacen copias del objeto, siempre se pasa
- por referencia.
-.. [#dslicing] Este problema se da en C++ cuando se pasa una clase derivada
- a una función que acepta una clase base por valor como parámetro. Al
- realizarse una copia de la clase con el constructor de copia de la clase
- base, se pierden (o *rebanan*) los atributos de la clase derivada, y la
- información de tipos en tiempo de ejecución (*RTTI*).
-.. [#dvalsem] Semántica de valor significa que el tipo es tratado como
- si fuera un valor concreto. En general se pasa por valor y se hacen
- copias a menos que se utilice explícitamente un puntero.
-.. [#dnan] Del inglés *Not A Number*, es un valor especial que indica que
- estamos ante un valor inválido.
-.. [#dmangle] *Name mangling* es el nombre dado comunmente a una técnica
- necesaria para poder sobrecargar nombres de símbolos. Consiste en
- codificar los nombres de las funciones tomando como entrada el nombre de
- la función y la cantidad y tipo de parámetros, asegurando que dos
- funciones con el mismo nombre pero distintos parámetros (sobrecargada)
- tengan nombres distintos.
+ en D_ se refiere a funciones miembro que pueden ser tratadas
+ sintácticamente como campos de esa clase/estructura.
+
+ Ejemplo::
+
+ class Foo {
+ int data() { return _data; } // propiedad de lectura
+ int data(int value) { return _data = value; } // de escritura
+ private int _data;
+ }
+ Foo f = new Foo;
+ f.data = 1; // llama a f.data(1)
+ int i = f.data; // llama a f.data()
+
+ Además tipos nativos, clases, estructuras y expresiones tienen
+ *properties* predefinidos, por ejemplo:
+
+ ``sizeof``:
+ tamaño ocupado en memoria (ejemplo: ``int.sizeof`` -> 4).
+
+ ``init``:
+ valor de inicialización por omisión (ejemplo: ``float.init`` -> *NaN*
+ [#dnan]_).
+
+ .. [#dnan] Del inglés *Not A Number*, es un valor especial que indica que
+ estamos ante un valor inválido.
+
+ ``stringof``:
+ representación textual del tipo (ejemplo: ``(1+2).stringof`` -> ``"1
+ + 2"``).
+
+ ``mangleof``:
+ representación textual del tipo *mutilado* [#dmangle]_.
+
+ .. [#dmangle] *Name mangling* es el nombre dado comunmente a una técnica
+ necesaria para poder sobrecargar nombres de símbolos. Consiste en
+ codificar los nombres de las funciones tomando como entrada el nombre de
+ la función y la cantidad y tipo de parámetros, asegurando que dos
+ funciones con el mismo nombre pero distintos parámetros (sobrecargada)
+ tengan nombres distintos.
+
+ ``alignof``
+ alineación de una estructura o tipo.
+
+ Estos son solo los *properties* predefinidos para todos los tipos, pero
+ hay una cantidad considerable de *properties* extra para cada tipo.
+
+ Más información sobre *properties* de clases en
+ http://www.digitalmars.com/d/1.0/property.html#classproperties y sobre
+ *properties* predefinidos en
+ http://www.digitalmars.com/d/1.0/property.html
+
Programación confiable
particular atención a esto y provee las siguientes herramientas:
Excepciones:
- D_ soporta excepciones de manera similar a Java_: provee ``try``,
- ``catch`` y ``finally``. Esto permite que los errores difícilmente pasen
- silenciosamente sin ser detectados.
+ D_ soporta excepciones de manera similar a Java_: provee ``try``,
+ ``catch`` y ``finally``. Esto permite que los errores difícilmente pasen
+ silenciosamente sin ser detectados.
``assert``:
- es una condición que debe cumplirse siempre en un programa, como un
- chequeo de integridad. Esto es muy utilizado en C/C++, donde ``assert()``
- es una *macro* que solo se compila cuando la *macro* ``NDEBUG`` no está
- definida. Esto permite eliminar los chequeos de integridad del programa,
- que pueden ser costosos, para versiones que se suponen estables.
+ es una condición que debe cumplirse siempre en un programa, como un
+ chequeo de integridad. Esto es muy utilizado en C/C++, donde
+ ``assert()`` es una *macro* que solo se compila cuando la *macro*
+ ``NDEBUG`` no está definida. Esto permite eliminar los chequeos de
+ integridad del programa, que pueden ser costosos, para versiones que se
+ suponen estables.
- D_ lleva este concepto más allá y hace al ``assert`` parte del lenguaje.
- Si una verificación no se cumple, lanza una excepción. El ``assert`` no
- es compilado cuando se utiliza una opción del compilador.
+ D_ lleva este concepto más allá y hace al ``assert`` parte del lenguaje.
+ Si una verificación no se cumple, lanza una excepción. El ``assert`` no
+ es compilado cuando se utiliza una opción del compilador.
- Ejemplo::
+ Ejemplo::
- File f = open("archivo");
- assert (f.ok());
+ File f = open("archivo");
+ assert (f.ok());
Diseño por contrato:
- el diseño por contrato es un concepto introducido por el lenguaje Eiffel_
- a mediados/finales de los '80. Se trata de incorporar en el lenguaje las
- herramientas para poder aplicar verificaciones formales a las interfaces
- de los programas.
-
- D_ implementa las siguientes formas de diseño por contrato (todas se
- ejecutan siempre y cuando no se compile en modo *release*, de manera de
- no sacrificar *performance* cuando es necesario):
+ el diseño por contrato es un concepto introducido por el lenguaje
+ Eiffel_ a mediados/finales de los '80. Se trata de incorporar en el
+ lenguaje las herramientas para poder aplicar verificaciones formales
+ a las interfaces de los programas.
+
+ D_ implementa las siguientes formas de diseño por contrato (todas se
+ ejecutan siempre y cuando no se compile en modo *release*, de manera de
+ no sacrificar *performance* cuando es necesario):
+
+ Pre y post condiciones:
+ Ejemplo::
+
+ double raiz_cuadrada(double x)
+ in { // pre-condiciones
+ assert (x >= 0.0);
+ }
+ out (resultado) { // post-condiciones
+ assert (resultado >= 0.0);
+ if (x < 1.0)
+ assert (resultado < x);
+ else if (x > 1.0)
+ assert (resultado > x);
+ else
+ assert (resultado == 1);
+ }
+ body {
+ // implementación
+ }
+
+ Invariantes de representación:
+ La invariante de representación es un método de una clase
+ o estructura que es verificada cuando se completa su construcción,
+ antes de la destrucción, antes y después de ejecutar cualquier
+ función miembro pública y cuando se lo requiere de forma explícita
+ utilizando ``assert``.
+
+ Ejemplo::
+
+ class Fecha {
+ int dia;
+ int hora;
+ invariant() {
+ assert(1 <= dia && dia <= 31);
+ assert(0 <= hora && hora < 24);
+ }
+ }
+
+ Más información en http://www.digitalmars.com/d/1.0/dbc.html
- ure y post condiciones:
- Ejemplo::
+Pruebas unitarias:
+ es posible incluir pequeñas pruebas unitarias en el lenguaje. Éstas son
+ ejecutadas (cuando no se compila en modo *release*) al comenzar el
+ programa, antes de que la función ``main()``.
- double raiz_cuadrada(double x)
- in { // pre-condiciones
- assert (x >= 0.0);
- }
- out (resultado) { // post-condiciones
- assert (resultado >= 0.0);
- if (x < 1.0)
- assert (resultado < x);
- else if (x > 1.0)
- assert (resultado > x);
- else
- assert (resultado == 1);
- }
- body {
- // implementación
- }
+ Ejemplo::
- Invariantes de representación:
- La invariante de representación es un método de una clase o estructura
- que es verificada cuando se completa su construcción, antes de la
- destrucción, antes y después de ejecutar cualquier función miembro
- pública y cuando se lo requiere de forma explícita utilizando
- ``assert``.
-
- Ejemplo::
-
- class Fecha {
- int dia;
- int hora;
- invariant() {
- assert(1 <= dia && dia <= 31);
- assert(0 <= hora && hora < 24);
- }
+ unittest {
+ Fecha fecha;
+ fecha.dia = 5;
+ assert (fecha.dia == 5);
+ assert (fecha);
}
- Más información en http://www.digitalmars.com/d/1.0/dbc.html
+Orden de construcción estática:
+ a diferencia de C++, D_ garantiza el orden de inicialización de los
+ módulos. Si bien en C++ no hay módulos si no unidades de compilación, es
+ posible que se ejecute código antes del ``main()`` en C++, si hay, por
+ ejemplo, instancias globales con un constructor definido. C++ no
+ garantiza un orden de inicialización, lo que trae muchos problemas. En
+ D_ se define el orden de inicialización y es el mismo orden en que el
+ usuario importa los módulos.
-Pruebas unitarias:
- es posible incluir pequeñas pruebas unitarias en el lenguaje. Éstas son
- ejecutadas (cuando no se compila en modo *release*) al comenzar el
- programa, antes de que la función ``main()``.
+Inicialización garantizada:
+ todas las variables son inicializadas por el lenguaje (a menos que el
+ usuario pida explícitamente que no lo sean). Siempre que sea posible se
+ elijen valores de inicialización que permitan saber al programador que
+ la variable no fue inicializada explícitamente, de manera de poder
+ detectar errores de manera temprana.
- Ejemplo::
+ Ejemplo::
- unittest {
- Fecha fecha;
- fecha.dia = 5;
- assert (fecha.dia == 5);
- assert (fecha);
- }
+ double d; // inicializado a NaN
+ int x; // inicializado a 0
+ Fecha f; // inicializado a null
+ byte[5] a; // inicializados todos los valores a 0
+ long l = void; // NO inicializado (explícitamente)
-Orden de construcción estática:
- a diferencia de C++, D_ garantiza el orden de inicialización de los
- módulos. Si bien en C++ no hay módulos si no unidades de compilación, es
- posible que se ejecute código antes del ``main()`` en C++, si hay, por
- ejemplo, instancias globales con un constructor definido. C++ no
- garantiza un orden de inicialización, lo que trae muchos problemas. En D_
- se define el orden de inicialización y es el mismo orden en que el
- usuario importa los módulos.
+*RAII* (*Resource Adquisition Is Initialization*):
+ es una técnica muy utilizada en C++ que consiste en reservar recursos
+ por medio de la construcción de un objeto y liberarlos cuando se libera
+ éste. Al llamarse al destructor de manera automática cuando se sale del
+ *scope*, se asegura que el recurso será liberado también.
-Inicialización garantizada:
- todas las variables son inicializadas por el lenguaje (a menos que el
- usuario pida explícitamente que no lo sean). Siempre que sea posible se
- elijen valores de inicialización que permitan saber al programador que la
- variable no fue inicializada explícitamente, de manera de poder detectar
- errores de manera temprana.
+ Esta técnica es la base para desarrollar código seguro en cuanto
+ a excepciones (*exception-safe*) [SUTT99]_.
- Ejemplo::
+ En D_ no es tan común utilizar *RAII* dada la existencia del recolector
+ de basura (en la mayoría de los casos el recurso a administrar es
+ sencillamente memoria). Sin embargo en los casos en donde es necesario,
+ puede utilizarse *RAII* mediante la utilización de la palabra reservada
+ ``scope``, que limita la vida de un objeto un bloque de código.
- double d; // inicializado a NaN
- int x; // inicializado a 0
- Fecha f; // inicializado a null
- byte[5] a; // inicializados todos los valores a 0
- long l = void; // NO inicializado (explícitamente)
+ Ejemplo::
-*RAII* (*Resource Adquisition Is Initialization*):
- es una técnica muy utilizada en C++ que consiste en reservar
- recursos por medio de la construcción de un objeto y liberarlos cuando
- se libera éste. Al llamarse al destructor de manera automática cuando
- se sale del *scope*, se asegura que el recurso será liberado también.
-
- Esta técnica es la base para desarrollar código seguro en cuanto a
- excepciones (*exception-safe*) [SUTT99]_.
-
- En D_ no es tan común utilizar *RAII* dada la existencia del recolector
- de basura (en la mayoría de los casos el recurso a administrar es
- sencillamente memoria). Sin embargo en los casos en donde es necesario,
- puede utilizarse *RAII* mediante la utilización de la palabra reservada
- ``scope``, que limita la vida de un objeto un bloque de código.
-
- Ejemplo::
-
- class Archivo {
- this() { /* adquiere recurso */ }
- ~this() { /* libera recurso */ }
- }
- void f() {
- scope Archivo archivo = new Archivo;
- // uso de archivo
- } // en este punto se llama al destructor de archivo
+ class Archivo {
+ this() { /* adquiere recurso */ }
+ ~this() { /* libera recurso */ }
+ }
+ void f() {
+ scope Archivo archivo = new Archivo;
+ // uso de archivo
+ } // en este punto se llama al destructor de archivo
Guardias de bloque (*scope guards*):
- además de poder limitar la vida de una instancia a un *scope*, es posible
- especificar un bloque de código arbitrario a ejecutar al abandonar un
- *scope*, ya sea cuando se sale del *scope* normalmente o por una falla.
-
- Ejemplo::
-
- int f(Lock lock) {
- lock.lock();
- scope (exit)
- lock.unlock(); // ejecutado siempre que salga de f()
- auto trans = new Transaccion;
- scope (success)
- trans.commit(); // ejecutado si sale con "return"
- scope (failure)
- trans.rollback(); // ejecutado si sale por una excepción
- if (condicion)
- throw Exception("error"); // ejecuta lock.unlock() y trans.rollback()
- else if (otra_condicion)
- return 5; // ejecuta lock.unlock() y trans.commit()
- return 0; // ejecuta lock.unlock() y trans.commit()
- }
-
- Esta es una nueva forma de poder escribir código *exception-safe*, aunque
- el programador debe tener un poco más de cuidado de especificar las
- acciones a ejecutar al finalizar el *scope*.
+ además de poder limitar la vida de una instancia a un *scope*, es
+ posible especificar un bloque de código arbitrario a ejecutar al
+ abandonar un *scope*, ya sea cuando se sale del *scope* normalmente
+ o por una falla.
+
+ Ejemplo::
+
+ int f(Lock lock) {
+ lock.lock();
+ scope (exit)
+ lock.unlock(); // ejecutado siempre que salga de f()
+ auto trans = new Transaccion;
+ scope (success)
+ trans.commit(); // ejecutado si sale con "return"
+ scope (failure)
+ trans.rollback(); // ejecutado si sale por una excepción
+ if (condicion)
+ throw Exception("error"); // ejecuta lock.unlock() y trans.rollback()
+ else if (otra_condicion)
+ return 5; // ejecuta lock.unlock() y trans.commit()
+ return 0; // ejecuta lock.unlock() y trans.commit()
+ }
+
+ Esta es una nueva forma de poder escribir código *exception-safe*,
+ aunque el programador debe tener un poco más de cuidado de especificar
+ las acciones a ejecutar al finalizar el *scope*.
Primitivas de sincronización de hilos:
- la programación multi-hilo está directamente soportada por el lenguaje,
- y se provee una primitiva de sincronización al igual que Java_. La
- palabra reservada ``synchronized`` puede aparecer como modificador de
- métodos (en cuyo caso se utiliza un *lock* por clase para sincronizar)
- o como una sentencia, en cuyo caso se crea un *lock* global por cada
- bloque ``synchronized`` a menos que se especifique sobre qué objeto
- realizar la sincronización. Por ejemplo::
-
- class Foo {
- synchronized void bar() { /* cuerpo */ }
- }
-
- Es equivalente a::
-
- class Foo {
- void bar() {
- synchronized (this) { /* cuerpo */ }
+ la programación multi-hilo está directamente soportada por el lenguaje,
+ y se provee una primitiva de sincronización al igual que Java_. La
+ palabra reservada ``synchronized`` puede aparecer como modificador de
+ métodos (en cuyo caso se utiliza un *lock* por clase para sincronizar)
+ o como una sentencia, en cuyo caso se crea un *lock* global por cada
+ bloque ``synchronized`` a menos que se especifique sobre qué objeto
+ realizar la sincronización. Por ejemplo::
+
+ class Foo {
+ synchronized void bar() { /* cuerpo */ }
+ }
+
+ Es equivalente a::
+
+ class Foo {
+ void bar() {
+ synchronized (this) { /* cuerpo */ }
+ }
}
- }
Compiladores
tanto en realidad hay solo un compilador disponible con 3 *back-ends*
[#backend]_ diferentes.
+.. [#frontend] *Front-end* es la parte del compilador encargada de hacer el
+ análisis léxico, sintáctico y semántico del código fuente, generando una
+ representación intermedia que luego el *back-end* convierte a código de
+ máquina.
+
+.. [#backend] El *back-end* es la parte del compilador encargada de convertir
+ la representación intermedia generada por el *front-end* a código de
+ máquina.
+
Con `DMD 1.041`__ se publicó el código fuente completo del compilador,
pero con una licencia muy restrictiva para uso personal, por lo que el
único efecto logrado por esto es que la gente pueda mandar parches
se terminó desarrollando el trabajo utilizando LDC_.
-.. [#frontend] *Front-end* es la parte del compilador encargada de hacer el
- análisis léxico, sintáctico y semántico del código fuente, generando una
- representación intermedia que luego el *back-end* convierte a código de
- máquina.
-.. [#backend] El *back-end* es la parte del compilador encargada de
- convertir la representación intermedia generada por el *front-end*
- a código de máquina.
-
.. include:: links.rst
-.. vim: set ts=2 sts=2 sw=2 et tw=75 :
+.. vim: set ts=3 sts=3 sw=3 et tw=78 :