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

Haskell · Cabal · herramientas de compilación

Los sandboxes de Cabal: qué fueron y qué los reemplazó

Por ColdwastActualizado el 13 de junio de 20269 min de lectura#haskell#cabal#build
Tres sandboxes de Cabal aislados, cada uno con su propio conjunto de paquetes, junto a una terminal ejecutando cabal sandbox init
Aislamiento de dependencias por proyecto — la idea que introdujeron los sandboxes, ahora el comportamiento por defecto.
Esta es una reescritura actualizada y mantenida por la comunidad de un tema por el que históricamente se conocía este dominio. Es contenido original y no está escrito por el anterior propietario del dominio ni afiliado a él. cabal sandbox es ya histórico — si solo quieres el flujo de trabajo moderno, salta a qué usar hoy.

Cuando cabal sandbox llegó en cabal-install 1.18 en agosto de 2013, arregló lo más doloroso del desarrollo en Haskell de entonces: la base de datos global de paquetes. Antes de los sandboxes, cada cabal install escribía en un único almacén compartido ~/.cabal. Instala dos proyectos que necesitaran versiones distintas de la misma biblioteca y te topabas con el infame "infierno de las dependencias" — una base de datos global corrupta que a menudo terminaba con rm -rf ~/.ghc ~/.cabal y empezar de cero.

Los sandboxes hicieron que las dependencias fueran por proyecto. Ese principio nunca desapareció — pero el mecanismo sí. Aquí tienes el panorama completo, desde cómo funcionaban los sandboxes hasta el flujo de trabajo al estilo Nix que los reemplazó.

Qué era realmente un sandbox

Un sandbox era una base de datos de paquetes local al proyecto. Creabas uno en el directorio de tu proyecto:

$ cd my-project
$ cabal sandbox init
Writing a default package environment file to
  /home/you/my-project/cabal.sandbox.config
Creating a new sandbox at
  /home/you/my-project/.cabal-sandbox

A partir de ahí, cabal install y cabal build ejecutados dentro de ese directorio usaban .cabal-sandbox/ en lugar del almacén global. Dos proyectos podían depender de versiones en conflicto de aeson o text sin interferir nunca, porque cada uno tenía su propia base de datos aislada.

Dependencias de fuentes locales

La otra característica genuinamente útil era add-source, que permitía a un sandbox incorporar una biblioteca directamente desde una copia local — algo inestimable cuando trabajas a la vez en una biblioteca y en una aplicación:

$ cabal sandbox add-source ../my-library
$ cabal install --dependencies-only
$ cabal build

Editabas ../my-library, recompilabas la aplicación y cabal detectaba el cambio y recompilaba la dependencia. Esto era lo más parecido a un flujo de trabajo de monorepo que tenía Haskell en 2013.

Por qué los sandboxes acabaron retirándose

Los sandboxes resolvían el aislamiento pero dejaban dos problemas sin resolver:

  • Sin compartición entre proyectos. Cada sandbox recompilaba el mundo desde cero. Diez proyectos que dependieran de lens significaban diez builds separadas de lens y su enorme árbol de dependencias — lento y voraz en disco.
  • Sin reproducibilidad entre máquinas. Un sandbox capturaba dónde vivían los paquetes, no un plan preciso y repetible de qué versiones exactas se elegirían. Dos desarrolladores ejecutando los mismos comandos con una semana de diferencia podían aun así obtener builds distintas.

La solución, tomada conceptualmente de Nix, fue hacer que las builds fueran direccionadas por contenido y compartidas globalmente: compilar cualquier combinación dada de paquete-versión-más-flags una vez, almacenarla bajo un hash en un almacén global inmutable y dejar que cada proyecto que necesite esa combinación exacta la reutilice. Eso son las "builds locales al estilo Nix", y llegaron como new-build en Cabal 2.0.

Qué usar hoy (2026)

Código fuente en la pantalla de un ordenador
Código fuente en la pantalla de un ordenador

cabal sandbox quedó obsoleto durante la serie Cabal 2.x y se eliminó por completo en versiones posteriores. El flujo de trabajo moderno y predeterminado del cabal-install actual no necesita ningún paso de inicialización — el aislamiento es automático:

# In any project directory with a .cabal file:
$ cabal build          # nix-style local build (v2 is the default now)
$ cabal run
$ cabal repl
$ cabal test

Lo que ocurre por debajo es exactamente el aislamiento por proyecto que los sandboxes prometían, más la compartición que les faltaba:

  • Los artefactos de build de tu proyecto van en un directorio local dist-newstyle/ (el sucesor espiritual de .cabal-sandbox/).
  • Las dependencias compiladas van en un almacén global inmutable (~/.cabal/store, o bajo ~/.local/state/cabal en las disposiciones más nuevas), indexadas por un hash del paquete, la versión, las dependencias y los flags. Compila lens una vez; cada proyecto que reutilice el mismo plan enlaza la misma entrada del almacén.
  • Un archivo cabal.project reemplaza a cabal.sandbox.config para la configuración, las builds multi-paquete y el pinning.

El equivalente moderno de add-source

Las dependencias de fuentes locales y remotas viven ahora en cabal.project:

# cabal.project
packages: . ../my-library

-- or pull a dependency straight from git:
source-repository-package
    type: git
    location: https://github.com/owner/some-lib
    tag: 0a1b2c3d

Esto es estrictamente más potente que add-source: maneja múltiples paquetes locales, pins exactos de git y builds reproducibles multi-repo.

Fijar versiones de forma reproducible

Para la reproducibilidad que los sandboxes nunca ofrecieron, genera un archivo freeze que bloquee cada versión transitiva:

$ cabal freeze        # writes cabal.project.freeze
# commit it — every machine now resolves the identical plan

¿Y qué hay de Stack?

Stack aborda los mismos objetivos desde otro ángulo: en lugar de resolver versiones por proyecto, compila contra snapshots curados (resolvers de Stackage como lts-22.x) donde se sabe que un conjunto entero de paquetes compila junto, y comparte las builds globalmente por snapshot. Ambas herramientas están sanas y muy usadas en 2026; la división práctica es a grandes rasgos "quiero el toolchain estándar y un control fino" (Cabal) frente a "quiero un conjunto curado y de confianza listo para usar" (Stack). Ninguna usa sandboxes — el concepto está totalmente superado en ambos lados.

Chuleta de migración

cabal sandbox init                 →  (nothing — just `cabal build`)
cabal sandbox add-source ../lib    →  packages: . ../lib   (in cabal.project)
cabal install --dependencies-only  →  cabal build --only-dependencies
.cabal-sandbox/                    →  dist-newstyle/ (local) + ~/.cabal/store (shared)
cabal.sandbox.config               →  cabal.project (+ cabal.project.freeze)

La lección que enseñaron los sandboxes — nunca contaminar un estado mutable global, aislar por proyecto — triunfó tan por completo que ahora es invisible: es simplemente cómo se comporta cabal build. Si estás leyendo esto porque un enlace o tutorial antiguo te apuntó a cabal sandbox init, puedes olvidarte tranquilamente del comando. Solo cabal build.


Relacionado: Cabal 2.0 y las builds al estilo Nix · Paralelizar cabal-install · todas las guías