🅳 D — 2001

⬅️ Atlas · 🗂️ Todas las fichas · 📚 Índice de clases

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++ llama constexpr y Rust llama const 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

📜 Historia

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:

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.

🏭 Dónde vive hoy

🧠 Lo que enseña: metaprogramación legible y contratos

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.

🔄 Lo que se ha modernizado

⚙️ Cómo se ejecuta hoy

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

🧪 El programa de la clase 041 en D

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.

📚 Fuentes y bibliografía


⏮️ Volver al Atlas · 🗂️ Todas las fichas · 🔗 Relacionadas: C++ · Nim · Zig · Rust · Ada