Haskell · Cabal · herramientas de compilación
Cabal 2.0: el lanzamiento que reformó las builds de Haskell
Cabal 2.0 se lanzó en septiembre de 2017 junto a GHC 8.2, y, en retrospectiva, es el lanzamiento que marcó el rumbo de las herramientas de Haskell durante la década siguiente. Es el momento en que tres cosas se hicieron realidad a la vez: builds locales al estilo Nix, Backpack y un formato .cabal moderno con una sintaxis de versiones más sensata. Aquí está qué fue cada una y dónde se encuentra hoy.
1. Builds locales al estilo Nix (new-build)
La característica estrella. Antes, los sandboxes aislaban las dependencias por proyecto pero recompilaban todo desde cero y no eran reproducibles. Cabal 2.0 promocionó cabal new-build: un modelo direccionado por contenido donde cada combinación única de paquete + versión + flags + dependencias se compila una vez, se almacena bajo un hash en un almacén global compartido y se reutiliza por cada proyecto cuyo plan de build la necesite.
$ cabal new-build # 2017 spelling
$ cabal build # 2026: this IS new-build — it became the default in Cabal 3.0 Estado en 2026: el prefijo new-* ya no existe. cabal build, cabal run, cabal repl y cabal test son todos al estilo Nix por defecto. Los alias v2- siguen existiendo para los scripts; los antiguos comandos v1- están obsoletos. Lo que Cabal 2.0 introdujo como opcional es ahora simplemente cómo funciona Cabal.
2. Backpack — paquetes mixin y firmas de módulo
Backpack añadió una genuina modularidad paramétrica a Haskell: una biblioteca puede declarar una firma de módulo (una interfaz) y ser comprobada de tipos contra ella sin comprometerse con una implementación concreta; un consumidor luego "mezcla" la implementación que quiere. El ejemplo clásico es una biblioteca escrita contra una firma abstracta Str que puede instanciarse con String, Text estricto o ByteString sin duplicar código.
-- in a .cabal file
library
signatures: Str
exposed-modules: Generic.Algorithm
library impl-text
mixins: indef-lib (Generic.Algorithm as Text.Algorithm)
requires (Str as Data.Text) Estado en 2026: Backpack funciona y sigue soportado, pero se quedó como una herramienta especializada en lugar de un patrón generalista — las clases de tipos y la parametrización ordinaria cubren la mayoría de las necesidades, y el soporte de herramientas/IDE para las firmas siguió siendo escaso. Recurre a él cuando de verdad necesites compilar el mismo código contra varios tipos concretos; de lo contrario, rara vez te lo encontrarás.
3. Un formato .cabal moderno
Establecer cabal-version: 2.0 desbloqueó mejoras de formato que ahora son Haskell de cada día:
- El operador caret
^>=.build-depends: aeson ^>= 2.1significa ">= 2.1 && < 2.2" — una forma compacta y reveladora de intención de seguir el límite superior del PVP. Está por todas partes en los paquetes modernos. autogen-modules, para que los módulos generados comoPaths_yourpkgse declaren correctamente y se incluyan en el sdist.- Bibliotecas foráneas (la stanza
foreign-library), que permite compilar código Haskell como un objeto compartido para que lo consuman otros lenguajes. build-dependspor componente, para que la biblioteca, los tests y los benchmarks de un paquete puedan tener cada uno su propio conjunto preciso de dependencias.
Estado en 2026: todo estándar. La recomendación ahora es establecer cabal-version: 3.0 o posterior (implica todo lo que daba 2.0, más las stanzas comunes de 2.2, el comodín ** para archivos de datos y más). Usa 2.0 solo si debes dar soporte a un toolchain muy antiguo.
4. El resolutor de dependencias mejoró
Cabal 2.0 incluyó mejoras del resolutor que lo hicieron a la vez más rápido y más propenso a encontrar un plan de instalación válido en un grafo de dependencias profundo, con explicaciones más claras cuando no existe ningún plan. El resolutor ha seguido mejorando desde entonces, pero 2.0 es donde "el resolutor normalmente simplemente funciona" empezó a ser cierto para proyectos no triviales.
Por qué importó
Antes de 2.0, la historia de las builds de Haskell era "instala globalmente y reza", parcheada por sandboxes. Después de 2.0, pasó a ser "declara lo que quieres, obtén una build reproducible, compartida y aislada" — el mismo modelo mental que Nix, Cargo o los gestores de paquetes modernos de otros entornos. Cada cabal build que ejecutes en 2026 es el descendiente directo de lo que 2.0 convirtió en el camino predeterminado.
Si actualizas un proyecto antiguo
# Bump the format and modernise bounds:
cabal-version: 3.0 # was 1.x or 2.0
# Replace hand-written ranges with caret bounds where it fits:
build-depends: base ^>= 4.18
, text ^>= 2.0
# Drop any v1-/sandbox era commands from CI; use:
cabal build --enable-tests
cabal test
cabal freeze # commit cabal.project.freeze for reproducibility