coldwa.st
Todas las guíasProgramaciónWebDatosHerramientasBases de datosHaskellConceptosCabal y buildsToolchainCompiladorRendimientoEditor y HLS

Haskell · Cabal · herramientas de compilación

Cabal 2.0: el lanzamiento que reformó las builds de Haskell

Por ColdwastActualizado el 13 de junio de 20267 min de lectura#haskell#cabal#build
Un grafo de dependencias y una terminal ejecutando cabal new-build, que ilustra las builds al estilo Nix de Cabal 2.0
Cabal 2.0 convirtió la planificación al estilo Nix y por proyecto en la base del toolchain moderno.
Reescritura actualizada y mantenida por la comunidad — contenido original, no escrito por el anterior propietario del dominio ni afiliado a él. Donde los hechos de 2017 han cambiado desde entonces, el estado en 2026 se señala explícitamente.

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.1 significa ">= 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 como Paths_yourpkg se 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-depends por 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ó

Código fuente abierto en un editor de código
Código fuente abierto en un editor de código

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

Relacionado: Los sandboxes de Cabal y lo que los reemplazó · Paralelizar cabal-install · todas las guías