D es el C++ que Walter Bright habría hecho si hubiera podido empezar de cero, y lo dice con autoridad: Bright escribió el primer compilador nativo de C++ del mundo. Es un lenguaje excelente, técnicamente adelantado a su tiempo en varias cosas, y el caso de estudio más claro de que la calidad técnica no basta (clase 164).
🎯 Por qué está en este programa
D es un primo de la familia C / llaves (Atlas), cuyo representante en el núcleo es C.
Aporta al programa una idea que hoy está en todas partes y que D tuvo primero: ejecutar código del propio lenguaje en tiempo de compilación —CTFE, de 2007—, que es lo que Zig llama
comptime, C++ llamaconstexpry Rust llamaconst fn(clase 122). Y aporta el ejemplo más didáctico de cómo una decisión de gobernanza puede hundir un lenguaje.
| Año | 2001; D2 en 2007, incompatible con D1; estable desde ~2010 |
| Autoría | Walter Bright (autor del compilador Zortech C++), con Andrei Alexandrescu |
| Familia | C / llaves; rediseño de C++ con influencia de Java y Python |
| Paradigma | Multiparadigma: imperativo, OO, funcional, genérico y metaprogramación |
| Tipado | Estático y fuerte, con inferencia, immutable y pure comprobados |
| Memoria | Recolector opcional: se puede usar, evitar (@nogc) o gestionar a mano |
| Ejecución | Compilado a nativo — tres compiladores: DMD (rápido), LDC (LLVM), GDC (GCC) |
| Estado | 🟡 Vivo y minoritario: excelente, con adopción industrial pequeña |
Walter Bright había escrito Zortech C++ (1987), el primer compilador de C++ que generaba código nativo directamente en vez de traducir a C. Nadie conocía mejor las esquinas del lenguaje ni sus problemas.
En 1999 empezó D: mantener lo bueno de C++ —rendimiento, acceso al sistema, control— y quitar la carga histórica: el preprocesador, la compatibilidad con C a nivel de fuente, los ficheros de cabecera, las plantillas ilegibles.
Y trajo cosas que en 2001 eran adelantadas:
#include (clase 149).unittest dentro del propio fichero (clase 139).in, out e invariant — la idea de Ada y Eiffel (clase 118).Y entonces vino el error que lo marcó: D2 (2007) rompió la compatibilidad con D1 en medio de la adopción, y la biblioteca estándar se partió en dos —Phobos y Tango— con la comunidad dividida. Cuando se resolvió, años después, el hueco lo estaban ocupando otros.
Es la lección más útil de esta ficha, y la clase 175 la desarrolla: la compatibilidad y la cohesión del ecosistema pesan más que las características. Go triunfó con menos características y una promesa de compatibilidad que ha cumplido.
CTFE — ejecutar el lenguaje al compilar:
int factorial(int n) pure { return n <= 1 ? 1 : n * factorial(n - 1); }
enum resultado = factorial(10); // ← calculado EN COMPILACIÓN, con la MISMA función
Y esa es la aportación: no hay un lenguaje de macros aparte. Una función normal, si es pure y
sus argumentos se conocen, se ejecuta al compilar. Es lo que C++ tardó hasta constexpr (2011)
en tener, y con mucha menos ceremonia.
Los contratos, que son la idea de Ada y de Eiffel (clase 118):
int dividir(int a, int b)
in { assert(b != 0); }
out (r) { assert(r * b <= a); }
do { return a / b; }
Las pruebas en el propio fichero, que resuelven un problema real de la clase 139:
unittest {
assert(dividir(10, 2) == 5);
}
Se compilan solo con -unittest, viven junto al código que prueban y no hay marco que
instalar. Es una decisión que muy pocos lenguajes han tomado —Rust y Go lo
hacen parecido— y que sube muchísimo la probabilidad de que las pruebas existan.
Y el recolector opcional, que es la respuesta de D a la tensión de la clase 131:
@nogc void funcionCritica() { /* el compilador PROHÍBE reservar aquí */ }
Se puede tener recolector donde no importa y prohibirlo donde sí, comprobado por el compilador.
@safe, @trusted, @system: subconjuntos de seguridad de memoria comprobados por el
compilador. @safe prohíbe la aritmética de punteros y las conversiones inseguras — la idea de la
clase 153 sin cambiar de lenguaje.@live), en la línea de Rust.betterC: usar D sin tiempo de ejecución ni recolector, para sistemas embebidos y para
integrarse en proyectos C.extern "C" — llamar a clases y plantillas
(clase 156).dmd -run main.d < entrada.txt # DMD: compilación rapidísima, para desarrollar
ldc2 -O3 main.d # LDC (LLVM): el que hay que usar en producción
dmd -unittest -main -run main.d # ← ejecutar las pruebas del propio fichero
dub build && dub test # con proyecto y dependencias
import std.stdio, std.array, std.conv, std.algorithm;
void main() {
auto v = readln().split().map!(to!double).array;
const total = v[0] * v[1] * (1 - v[2]);
writefln("Total: %.2f", total);
}
Lo que hay que ver.
map!(to!double) usa ! para los argumentos de plantilla: to!double es la función de
conversión instanciada para double. Es la sintaxis de genéricos de D, mucho más ligera que
<...> de C++, y se resuelve en compilación sin coste.readln().split().map!(...).array es el estilo de rangos de D — los ranges son la
respuesta de D a los iteradores de C++, y son perezosos: nada se calcula hasta el .array final
(clase 115).const total es inmutabilidad real, comprobada por el compilador y transitiva: en D,
immutable significa que nada alcanzable desde ahí puede cambiar (clase 102), que es más fuerte
que el const de C++.auto infiere el tipo, y el programa no pierde ni una comprobación por ello: sigue siendo
estático y fuerte.⏮️ Volver al Atlas · 🗂️ Todas las fichas · 🔗 Relacionadas: C++ · Nim · Zig · Rust · Ada