⬅️ Volver a la clase 088 · 🧬 Primos del Atlas · 🧟 Índice de lenguajes vivos · 📚 Índice
El valor absoluto, calculado por algo que vive en otro sitio. La última clase de la Parte 5 cierra el
recorrido: cómo se junta el trabajo de varias personas en un solo programa. Y la respuesta separa
a estos lenguajes en dos épocas: los que copian texto —COPY, %INCLUDE, #include— y los que
enlazan unidades compiladas con su interfaz comprobada.
🎯 Estos lenguajes no están aquí por ser antiguos
El criterio es doble y se declara en la ficha de cada uno: que se ejecute hoy —banca, sanidad, aviónica, ERP, diseño de chips, CAD— y que deje a la vista un concepto que los diez del núcleo esconden.
Aquí el concepto es la organización de un proyecto, y estos lenguajes lo enseñan porque cargan con la solución antigua y con la moderna a la vez. COBOL con
COPY, PL/I con%INCLUDEy C++ con#includeson sustitución textual: sin comprobación entre unidades, con recompilación en cascada y con el orden importando.Enfrente, Ada con
with, Fortran conuse, Pascal conusesy RPG con los directorios de enlace compilan cada unidad por separado y comprueban que encajen. Ada lo hacía en 1983 y C++ lo consiguió en 2020.Y ninguno es una foto fija: casi todos han incorporado en los últimos años JSON, REST, GPU, Unicode o Git. Cada ficha lo detalla en su sección
🔄 Lo que se ha modernizado.
n → stdout: abs=<|n|>abs(n) = |n|| stdin | esperado |
|---|---|
-5 |
abs=5 |
3 |
abs=3 |
0 |
abs=0 |
Qué está verificado en esta página. Los lenguajes de la sección 🟢 se ejecutan en CI contra este mismo
casos.json, igual que las diez implementaciones del núcleo (workflow Labs). Los de la sección 🟡 no pueden cumplir este contrato sin falsear el lenguaje, y se explica por qué. Los de la sección ⚪ sí podrían, pero su cadena de herramientas no está en los runners: son correctos, sin sello de máquina.
Ficha completa · Banca, seguros, gobierno, medios de pago · cobc -x -free prog.cob
IDENTIFICATION DIVISION.
PROGRAM-ID. ABSOLUTO.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 LINEA PIC X(80).
01 N PIC S9(9) COMP-3.
01 R PIC 9(9) COMP-3.
01 ED-R PIC Z(8)9.
PROCEDURE DIVISION.
ACCEPT LINEA
MOVE FUNCTION NUMVAL(LINEA) TO N
COMPUTE R = FUNCTION ABS(N)
MOVE R TO ED-R
DISPLAY "abs=" FUNCTION TRIM(ED-R)
STOP RUN.
Lo que esta clase enseña en COBOL. COBOL tiene dos formas de reutilizar, y son de naturalezas opuestas.
COPY es inclusión textual en tiempo de compilación, como el #include de C:
COPY CLIENTE.
COPY CLIENTE REPLACING ==:PRE:== BY ==CLI==.
Comparte definiciones de datos, y su versión con REPLACING es el genérico de los pobres de la
clase 078. Sus problemas son los conocidos: cambiar un copybook obliga a recompilar todos los
programas que lo usan, y nadie garantiza que se haga.
Ese es un problema operativo serio en un sistema con miles de programas, y la solución es una
herramienta de gestión de dependencias —IBM Dependency Based Build, Endevor, Changeman— que mantiene
un grafo de qué programa usa qué copybook y decide qué recompilar. Es un make para mainframe, y
lleva funcionando décadas.
CALL es lo contrario: enlaza con un programa compilado por separado, y puede ser estático
—resuelto al enlazar— o dinámico —resuelto al ejecutar, clase 085—. Cambiar un subprograma llamado
dinámicamente no obliga a recompilar a sus clientes.
Esa distinción define la arquitectura de un sistema COBOL: los datos se comparten por texto y el código por enlace. Y explica por qué los copybooks se tratan con tanto cuidado: son la única dependencia que se propaga a la compilación.
Ficha completa · HPC, clima, física, BLAS/LAPACK · gfortran -O2 prog.f90
module matematicas
implicit none
private
public :: valor_absoluto
contains
pure function valor_absoluto(x) result(r)
integer, intent(in) :: x
integer :: r
r = abs(x)
end function valor_absoluto
end module matematicas
program absoluto
use matematicas, only: valor_absoluto ! importación SELECTIVA
implicit none
integer :: n
read(*, *) n
write(*, '(A,I0)') 'abs=', valor_absoluto(n)
end program absoluto
Lo que esta clase enseña en Fortran. use modulo, only: nombre es la importación de Fortran, y la
cláusula only es la práctica recomendada: importa solo lo que se usa, y la dependencia queda
documentada en la propia línea.
Y hay una forma más, para resolver colisiones:
use matematicas, only: mi_abs => valor_absoluto ! RENOMBRA al importar
use otro_modulo, only: valor_absoluto
El operador => renombra, así que dos módulos con el mismo nombre pueden convivir. Es lo mismo que
as en Python y use x as y en Rust.
Lo que distingue a Fortran en esta clase es una consecuencia práctica de los módulos: el orden de
compilación importa. Un módulo tiene que estar compilado antes que quien lo usa, porque el
compilador genera un fichero .mod con su interfaz que el cliente necesita leer.
En un proyecto con cien módulos, calcular ese orden a mano es inviable, y de ahí que Fortran tenga un
ecosistema entero de herramientas para deducir el grafo de dependencias —makedepf90, y hoy
fpm, el gestor de paquetes oficial—:
name = "mi_proyecto"
[dependencies]
stdlib = { git = "https://github.com/fortran-lang/stdlib" }
fpm deduce el orden solo, compila y gestiona dependencias externas. Que un lenguaje de 1957
consiguiera su gestor de paquetes en 2020 es tarde, y es exactamente el tipo de modernización que esta
sección quiere mostrar.
Ficha completa · Aviónica, espacio, ferrocarril, defensa · gnatmake prog.adb
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
procedure Absoluto is
package Matematicas is
function Valor_Absoluto (X : Integer) return Integer;
end Matematicas;
package body Matematicas is
function Valor_Absoluto (X : Integer) return Integer is
(if X < 0 then -X else X);
end Matematicas;
N : Integer;
begin
Get (N);
Put ("abs=");
Put (Matematicas.Valor_Absoluto (N), Width => 1);
New_Line;
end Absoluto;
Lo que esta clase enseña en Ada. Ada separa dos cosas que en casi todos los lenguajes van juntas, y esa separación es de las mejores decisiones de su diseño:
with Ada.Text_IO; -- DEPENDENCIA: "necesito compilar contra esto"
use Ada.Text_IO; -- VISIBILIDAD: "y quiero sus nombres sin cualificar"
with declara la dependencia y es lo único que necesita el compilador. use es opcional y solo
afecta a la comodidad de escritura. Se puede tener with sin use y escribir
Ada.Text_IO.Put_Line (...) completo, que es lo que hacen las guías de estilo para código crítico —
porque el sitio de la llamada dice de dónde viene cada cosa.
Compara con #include de C++, que hace las dos cosas a la vez y sin control, o con import de
Python, donde from x import * es la opción desaconsejada.
Y Ada tiene una regla que evita el problema clásico de use: si dos paquetes usados exportan el
mismo nombre, ninguno de los dos es visible y hay que cualificar. El compilador se niega en
lugar de elegir por ti, que es lo contrario de la resolución de sobrecarga de C++.
Sobre la organización, Ada impone una correspondencia estricta que GNAT convierte en regla de
ficheros: paquete.ads para la especificación, paquete.adb para el cuerpo, y los subpaquetes
jerárquicos con guion — matematicas-vectores.ads para Matematicas.Vectores.
Y Alire (alr), el gestor de paquetes moderno, completa el cuadro: alr with aws añade una
dependencia, resuelve versiones y compila. Llegó en 2018.
Ficha completa · Escritorio empresarial, TPV, industria (vía Delphi y Free Pascal) · fpc -Mobjfpc prog.pas
program Absoluto;
{$MODE OBJFPC}{$H+}
uses SysUtils, Math; { unidades: SysUtils para IntToStr, Math para Abs }
var
N: Integer;
begin
Read(N);
WriteLn('abs=', IntToStr(Abs(N)));
end.
Lo que esta clase enseña en Pascal. La cláusula uses es la importación, y tiene una
propiedad que la distingue de casi todas las demás: importa TODO lo público de la unidad, sin
selección.
No hay uses Math only (Abs). Y como consecuencia, el orden importa: si dos unidades declaran el
mismo nombre, gana la última de la lista.
uses UnidadA, UnidadB; { si las dos declaran Doblar, se usa la de UnidadB }
Esa regla —"gana la última"— es sencilla y es una fuente real de errores al añadir una unidad a un
programa grande. La solución es cualificar: UnidadA.Doblar(x).
Y Pascal distingue dos secciones uses, lo que sí es un acierto:
unit Mi;
interface
uses A; { A es visible para QUIEN USE Mi }
implementation
uses B; { B es un detalle interno; nadie más lo ve }
Poner una dependencia en implementation la mantiene privada y rompe las dependencias circulares:
dos unidades pueden usarse mutuamente si al menos una lo hace desde implementation. Es un mecanismo
que Ada resuelve con limited with y que C++ resuelve con declaraciones adelantadas.
El ecosistema moderno lo completa con el Online Package Manager de Lazarus y con fpcupdeluxe, y Delphi con GetIt — gestores de paquetes que llegaron tarde, como en casi todos los lenguajes de esta página.
Ficha completa · IA simbólica, CAD, investigación · sbcl --script prog.lisp
(let ((n (read)))
(format t "abs=~D~%" (abs n)))
Lo que esta clase enseña en Common Lisp. El estándar de Common Lisp no dice nada sobre ficheros,
compilación ni proyectos: define defpackage para los nombres (clase 086) y nada más. Cómo se
carga el código es problema del entorno.
Esa laguna la llenó ASDF (Another System Definition Facility), que es el make del mundo Lisp:
(defsystem "mi-proyecto"
:depends-on ("alexandria" "cl-ppcre")
:components ((:file "paquetes")
(:file "utiles" :depends-on ("paquetes"))
(:file "principal" :depends-on ("utiles"))))
Un fichero .asd declara los componentes, sus dependencias y el orden. ASDF calcula el grafo,
recompila lo necesario y lo carga.
Y encima está Quicklisp, que resuelve la distribución: (ql:quickload "cl-ppcre") descarga,
compila y carga una biblioteca y todas sus dependencias.
Lo interesante es la separación en tres capas, que Lisp hace más explícita que nadie:
| Capa | Qué resuelve | En Lisp |
|---|---|---|
| Espacio de nombres | Colisión de nombres | defpackage (en el lenguaje) |
| Sistema de construcción | Orden y compilación | ASDF (biblioteca) |
| Gestor de paquetes | Distribución y versiones | Quicklisp (externo) |
En Python son import, setuptools y pip; en Rust, mod, cargo build y crates.io. Lo que en
Rust es una sola herramienta, en Lisp son tres piezas independientes — con la ventaja de que se pueden
sustituir por separado, y el inconveniente de que hay que conocer las tres.
Ficha completa · Diseño de chips (EDA), redes, testing · tclsh prog.tcl
namespace eval ::matematicas {
namespace export valor_absoluto
proc valor_absoluto {x} { return [expr {abs($x)}] }
}
namespace import ::matematicas::valor_absoluto ;# importar al espacio actual
gets stdin linea
set n [string trim $linea]
puts "abs=[valor_absoluto $n]"
Lo que esta clase enseña en Tcl. Tcl separa con claridad la carga de la visibilidad, igual
que Ada separa with de use:
package require http 2.9 ;# CARGAR, con requisito de VERSIÓN
namespace import ::http::* ;# hacer visibles sus nombres aquí
package require busca el paquete en la ruta de bibliotecas, comprueba la versión y lo carga una
sola vez. Y el mecanismo que lo hace posible es el índice de paquetes:
# pkgIndex.tcl -- generado automáticamente
package ifneeded http 2.9.5 [list source [file join $dir http.tcl]]
package ifneeded registra cómo cargar un paquete sin cargarlo. Tcl solo ejecuta ese guion si
alguien lo pide, así que un directorio con doscientos paquetes se indexa sin cargar ninguno. Es carga
perezosa a nivel de proyecto, y funciona desde 1996.
Y namespace import copia los nombres exportados al espacio actual, con la posibilidad de
renombrar:
namespace import ::matematicas::valor_absoluto
namespace import {*}[namespace children ::plugins]
namespace forget ::matematicas::* ;# deshacer la importación
namespace forget no tiene equivalente en la mayoría de los lenguajes: retira una importación. En
un intérprete vivo, donde el estado persiste entre cargas, poder deshacer importa.
Para la distribución, el ecosistema tiene teapot y TEA (Tcl Extension Architecture), y los Starkits de la clase 041 permiten empaquetar aplicación, intérprete y paquetes en un solo fichero ejecutable.
Ficha completa · Sysadmin, texto, bioinformática · perl prog.pl
use strict;
use warnings;
package Matematicas;
sub valor_absoluto { return abs($_[0]) }
package main;
my $n = <STDIN>;
chomp $n;
print "abs=", Matematicas::valor_absoluto($n), "\n";
Lo que esta clase enseña en Perl. Como se vio en la clase 086, use es require más import,
y esa segunda mitad es lo que hace peculiar a Perl: import es un método normal que el módulo
define.
use Mi::Modulo qw(foo bar);
# equivale a:
# BEGIN { require Mi::Modulo; Mi::Modulo->import('foo', 'bar'); }
El módulo decide qué hacer con esos argumentos. Casi todos heredan de Exporter:
package Matematicas;
use Exporter 'import';
our @EXPORT_OK = qw(valor_absoluto); # se exportan si se piden
our @EXPORT = qw(); # se exportan SIEMPRE (desaconsejado)
our %EXPORT_TAGS = (todo => [@EXPORT_OK]); # grupos: use X qw(:todo)
La distinción entre @EXPORT y @EXPORT_OK es la lección: @EXPORT contamina el espacio del
cliente sin que lo pida, y la práctica moderna es dejarlo vacío y usar solo @EXPORT_OK.
Y el ecosistema es la aportación histórica de Perl a esta clase: CPAN, de 1995, fue el primer gran repositorio de módulos de la historia, y el modelo del que salieron PyPI, npm, RubyGems y crates.io.
Lo que CPAN tiene y casi ningún otro ecosistema ha replicado es CPAN Testers: una red de voluntarios que ejecuta automáticamente la batería de pruebas de cada módulo en decenas de combinaciones de sistema operativo y versión de Perl, y publica los resultados. Antes de instalar algo se puede consultar si pasa en tu plataforma exacta.
Para fijar versiones por proyecto están cpanfile y Carton, el equivalente de un lockfile.
Ficha completa · Videojuegos, navegadores, finanzas, HPC · g++ -std=c++17 prog.cpp
#include <cstdlib>
#include <iostream>
namespace matematicas {
int valor_absoluto(int x) { return std::abs(x); }
}
int main() {
int n{};
if (!(std::cin >> n)) return 1;
std::cout << "abs=" << matematicas::valor_absoluto(n) << '\n';
return 0;
}
Lo que esta clase enseña en C++. #include es sustitución textual, y durante cuarenta años ha
sido la única forma de compartir código en C y C++. Sus consecuencias, ya apuntadas en la clase 086,
son medibles: un .cpp que incluya <iostream> compila decenas de miles de líneas.
De ahí una colección entera de técnicas que existen solo para mitigar el #include:
#pragma once // guardas de inclusión
class Widget; // declaración adelantada: evita incluir la cabecera
class Impl; // modismo PIMPL: ocultar la implementación
// unity builds, cabeceras precompiladas, include-what-you-use...
Los módulos de C++20 resuelven el problema de raíz —una unidad compilada, importada ya analizada— y su adopción avanza despacio porque exige que el sistema de construcción entienda el grafo de dependencias entre módulos, que no se puede deducir del texto tan fácilmente.
Y el ecosistema es la otra mitad de esta clase. C++ estuvo treinta años sin gestor de paquetes, y hoy hay dos que compiten:
vcpkg install fmt # Microsoft, orientado a bibliotecas del sistema
conan install . # con perfiles y binarios precompilados
Que llegaran tan tarde explica una peculiaridad cultural del mundo C++: la costumbre de incluir las dependencias como submódulos de Git o de copiarlas al repositorio, que en Rust o Node sería impensable.
Y std::abs para enteros está en <cstdlib>, no en <cmath> — un detalle heredado de C que sigue
sorprendiendo.
Estos lenguajes no pueden leer de stdin y escribir en stdout sin dejar de ser ellos
mismos. No es una limitación del material: es su naturaleza. El cálculo es el mismo y la forma
de entrar y salir es la de su anfitrión. No pasan por el verificador, y se dice.
Ficha completa · IBM i: ERP, retail, logística, manufactura · CRTBNDRPG sobre IBM i
En IBM i un programa recibe sus datos por parámetros, por un fichero o por una pantalla, nunca por la entrada estándar.
**free
ctl-opt dftactgrp(*no) actgrp(*caller);
dcl-pi ABSOLUTO;
n int(10) const;
end-pi;
dcl-s salida char(30);
salida = 'abs=' + %char(valorAbsoluto(n));
dsply salida;
*inlr = *on;
return;
// En un proyecto real: otro módulo con `export`, enlazado en un
// PROGRAMA DE SERVICIO y localizado por un DIRECTORIO DE ENLACE.
dcl-proc valorAbsoluto;
dcl-pi *n int(10);
x int(10) const;
end-pi;
return %abs(x);
end-proc;
Lo que esta clase enseña en RPG. La organización de un proyecto en IBM i tiene cuatro piezas que ya aparecieron en la clase 086, y aquí se ven en su papel de "importar":
/COPY o /INCLUDE — inclusión textual de prototipos, como el COPY de COBOL. Es como se
comparten las declaraciones dcl-pr.*BNDDIR) — dónde buscar lo que falte al enlazar.La cuarta es la que hace de "ruta de búsqueda": se declara en el propio fuente y el enlazador la consulta.
ctl-opt bnddir('MIAPP/UTILES');
/include qrpgleref,prototipos
Con eso, valorAbsoluto se resuelve automáticamente en el programa de servicio correspondiente, sin
enumerar módulos.
Y hay una diferencia importante con casi todos los lenguajes de esta página: el enlace por defecto es dinámico. Un programa de servicio se carga en tiempo de ejecución y se comparte entre todos los trabajos que lo usen. Corregir un error es recompilar el programa de servicio; los programas que lo llaman no se tocan, siempre que la firma de la clase 086 no cambie.
Esa propiedad —actualizar una biblioteca sin recompilar a sus clientes, con verificación de firma al
cargar— es exactamente lo que en Linux dan los soname, y en IBM i está integrada en el sistema desde
1993.
El ecosistema moderno añade Git, bob para construcción reproducible y iproj.json para
declarar la estructura del proyecto desde VS Code.
Sí podrían cumplir el contrato, pero su cadena de herramientas no está en los runners de CI.
Ficha completa · Mainframe z/OS: banca, seguros · IBM Enterprise PL/I for z/OS
absoluto: procedure options(main);
declare n fixed binary(31);
get list (n);
put skip list ('abs=' || trim(char(valor_absoluto(n))));
valor_absoluto: procedure (x) returns (fixed binary(31));
declare x fixed binary(31);
return (abs(x));
end valor_absoluto;
end absoluto;
Lo que esta clase enseña en PL/I. PL/I organiza un proyecto con las dos herramientas de su época, y las dos son las antiguas:
%INCLUDE para las declaraciones —inclusión textual, con todos los problemas del COPY de COBOL
y del #include de C—:
%include declaraciones;
%include registro_cliente;
Y procedimientos externos enlazados por nombre, sin comprobación de firma entre unidades. Si un
programa declara entry (fixed binary(31)) y el procedimiento real espera fixed decimal(15,2), el
enlazador empareja los nombres igualmente y el resultado es corrupción de datos.
Esa es la carencia que Ada resolvió en 1983 con la comprobación entre unidades de compilación, y es uno de los argumentos más fuertes a favor de los módulos.
Lo que sí tiene el mundo PL/I, y compensa parte del problema, es la infraestructura del mainframe: los gestores de cambio —Endevor, Changeman, IBM DBB— mantienen el grafo de dependencias entre fuentes e includes, y recompilan en cascada lo que haga falta. Es un sistema de construcción externo al lenguaje, y lleva funcionando desde los 80.
El preprocesador de PL/I merece una nota final, porque va mucho más allá de %INCLUDE: tiene
variables, condicionales, bucles y procedimientos propios, ejecutados en tiempo de compilación.
%declare depurar fixed;
%depurar = 1;
%if depurar = 1 %then %do;
put skip list ('traza');
%end;
Es un lenguaje completo dentro del lenguaje, y es el antepasado directo de la metaprogramación por preprocesador — con las mismas virtudes y los mismos abusos que hoy se le reprochan al de C.
Ficha completa · Sanidad: historia clínica, VistA, Epic · YottaDB
ABSOLUTO ; Importar y organizar -- clase 088
read n
write "abs=", $$abs^ABSOLUTO(n), !
quit
;
abs(x) ; valor absoluto
quit $select(x<0 : -x, 1 : x)
Lo que esta clase enseña en M. M no tiene importación. No hay import, use, require ni
with: todas las rutinas del entorno son visibles desde cualquier rutina, y se invocan con
ETIQUETA^RUTINA.
do PROCESAR^FACTURA(id)
set x = $$CALCULAR^UTIL(a, b)
El ^ separa la etiqueta del nombre de la rutina, y no hace falta declarar nada. Es el modelo más
simple posible, y tiene el problema de la clase 086: un único espacio de nombres global para todo
el sistema.
Y sin embargo hay una propiedad que compensa parcialmente y que conviene entender: la carga es
automática y perezosa. La primera vez que se ejecuta do X^Y, el sistema busca Y, la carga en el
área de rutinas del proceso y la ejecuta. No hay fase de enlace, no hay ejecutable y no hay
recompilación en cascada: corregir una rutina afecta a todo el sistema en la siguiente llamada.
Eso hace que el despliegue en M sea trivial —copiar una rutina y ya está— y explica que los sistemas de M lleven décadas sin reiniciarse.
El ecosistema moderno ha añadido lo que faltaba: YottaDB tiene un gestor de paquetes y una
estructura de proyecto con directorios de rutinas y variables de entorno ($ZROUTINES) que definen la
ruta de búsqueda — algo parecido a un PATH para código.
Y en VistA, la distribución tiene su propio nombre y su propio formato: los KIDS builds (Kernel Installation and Distribution System), que empaquetan rutinas, definiciones de FileMan y guiones de instalación en un fichero que se despliega en cientos de hospitales. Es un gestor de paquetes de dominio específico, construido sobre un lenguaje que no tiene ninguno.
Ficha completa · Banca, seguros, trading · Pharo
| n |
n := stdin nextLine trimBoth asNumber.
Transcript show: 'abs=', n abs printString; cr.
Lo que esta clase enseña en Smalltalk. En Smalltalk no se importa nada. Todas las clases del
sistema están siempre disponibles en el diccionario Smalltalk, así que n abs funciona sin
declarar ninguna dependencia.
Eso es consecuencia directa del modelo de imagen de la clase 041: no hay ficheros que enlazar porque todo el sistema ya está cargado y vivo. La pregunta "¿de dónde viene esta clase?" se responde con una herramienta, no con una línea de código.
Y ahí está el problema que la comunidad tardó veinte años en resolver: si no hay dependencias declaradas, ¿cómo se distribuye un proyecto? La imagen es un binario de cientos de megas con todo mezclado.
La respuesta es una pila de herramientas que hoy funciona bien:
| Herramienta | Qué resuelve |
|---|---|
| Monticello | Versionado de paquetes; el "commit" de Smalltalk |
| Metacello | Declaración de dependencias y versiones, el package.json |
| Tonel | Guardar el código como ficheros de texto legibles por Git |
| Iceberg | Integración con Git y GitHub desde dentro de la imagen |
Una especificación de Metacello se parece bastante a lo que se espera hoy:
spec baseline: 'MiProyecto' with: [
spec repository: 'github://usuario/proyecto:main/src' ].
Y Tonel es la pieza decisiva: convirtió el código de Smalltalk en ficheros de texto, uno por clase, y con eso lo hizo compatible con Git, con las revisiones por pull request y con la integración continua.
Es la respuesta a la crítica más justificada que recibió Smalltalk durante décadas —"no encaja en un flujo moderno"— y llegó, como tantas cosas de esta sección, mucho después de que el lenguaje se diera por muerto.
Lo transferible: la unidad de reutilización determina la arquitectura. Si compartir código es
copiar texto, la unidad natural es el fichero y los proyectos acaban con cientos de cabeceras
interdependientes. Si es una unidad compilada con interfaz, la unidad natural es el módulo y las
dependencias se ven. Y si además hay un gestor de paquetes —fpm, Alire, Quicklisp, CPAN, vcpkg— la
unidad pasa a ser la biblioteca versionada. Los tres niveles conviven hoy en casi todos estos
lenguajes, y saber en cuál estás explica por qué tu compilación tarda lo que tarda.
⏮️ Volver a la clase 088 · 🧬 Los primos del Atlas · 🧟 Índice de lenguajes vivos