Haskell · Cabal · Build-Werkzeuge
Cabal-Sandboxes: Was sie waren und was sie ersetzt hat
cabal sandbox selbst gehört inzwischen der Geschichte an — wenn Sie nur den modernen Workflow wollen, springen Sie direkt zu was Sie heute verwenden sollten.Als cabal sandbox im August 2013 in cabal-install 1.18 erschien, behob es das damals Lästigste an der Haskell-Entwicklung: die globale Paketdatenbank. Vor den Sandboxes schrieb jedes cabal install in einen einzigen, gemeinsam genutzten ~/.cabal-Store. Installieren Sie zwei Projekte, die unterschiedliche Versionen derselben Bibliothek benötigen, und Sie landeten in der berüchtigten „dependency hell“ — einer beschädigten globalen Datenbank, die oft mit rm -rf ~/.ghc ~/.cabal und einem Neuanfang endete.
Sandboxes machten Abhängigkeiten projektbezogen. Dieses Prinzip ist nie verschwunden — der Mechanismus aber schon. Hier das vollständige Bild, vom Funktionieren der Sandboxes bis zum nix-artigen Workflow, der sie ersetzt hat.
Was eine Sandbox eigentlich war
Eine Sandbox war eine projektlokale Paketdatenbank. Sie erstellten eine im Verzeichnis Ihres Projekts:
$ 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 Von da an verwendeten cabal install und cabal build, in diesem Verzeichnis ausgeführt, .cabal-sandbox/ anstelle des globalen Stores. Zwei Projekte konnten von widersprüchlichen Versionen von aeson oder text abhängen, ohne sich je in die Quere zu kommen, weil jedes seine eigene isolierte Datenbank hatte.
Lokale Quell-Abhängigkeiten
Die andere wirklich nützliche Funktion war add-source, die es einer Sandbox erlaubte, eine Bibliothek direkt aus einem lokalen Repository einzubinden — unschätzbar, wenn man an einer Bibliothek und einer Anwendung zugleich bastelt:
$ cabal sandbox add-source ../my-library
$ cabal install --dependencies-only
$ cabal build Ändern Sie ../my-library, bauen Sie die Anwendung neu, und cabal erkannte die Änderung und kompilierte die Abhängigkeit neu. Das war 2013 das Nächste, was Haskell einem Monorepo-Workflow hatte.
Warum Sandboxes schließlich entfernt wurden
Sandboxes lösten die Isolation, ließen aber zwei Probleme ungelöst:
- Keine projektübergreifende gemeinsame Nutzung. Jede Sandbox baute die Welt von Grund auf neu. Zehn Projekte, die von
lensabhingen, bedeuteten zehn separate Builds vonlensund seinem riesigen Abhängigkeitsbaum — langsam und plattenhungrig. - Keine maschinenübergreifende Reproduzierbarkeit. Eine Sandbox erfasste, wo die Pakete lagen, nicht einen präzisen, reproduzierbaren Plan, welche genauen Versionen gewählt würden. Zwei Entwickler, die dieselben Befehle eine Woche später ausführten, konnten trotzdem unterschiedliche Builds erhalten.
Die Lösung, konzeptionell von Nix entlehnt, bestand darin, Builds inhaltsadressiert und global geteilt zu machen: eine gegebene Paket-Version-Optionen-Kombination einmal bauen, sie unter einem Hash in einem unveränderlichen globalen Store ablegen, und jedes Projekt, das genau diese Kombination braucht, sie wiederverwenden lassen. Das sind die „nix-artigen lokalen Builds“, die unter dem Namen new-build in Cabal 2.0 erschienen.
Was Sie heute verwenden sollten (2026)
cabal sandbox wurde während der Cabal-2.x-Reihe als veraltet markiert und in späteren Versionen vollständig entfernt. Der moderne Standard-Workflow des heutigen cabal-install erfordert keinen init-Schritt — die Isolation ist automatisch:
# 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 Was unter der Haube geschieht, ist genau die projektbezogene Isolation, die Sandboxes versprachen, plus die gemeinsame Nutzung, die ihnen fehlte:
- Die Build-Artefakte Ihres Projekts landen in einem lokalen Verzeichnis
dist-newstyle/(der geistige Nachfolger von.cabal-sandbox/). - Kompilierte Abhängigkeiten landen in einem unveränderlichen globalen Store (
~/.cabal/storeoder unter~/.local/state/cabalin neueren Layouts), indiziert über einen Hash aus Paket, Version, Abhängigkeiten und Optionen. Bauen Sielenseinmal; jedes Projekt, das denselben Plan wiederverwendet, referenziert denselben Store-Eintrag. - Eine Datei
cabal.projectersetztcabal.sandbox.configfür Konfiguration, Multi-Paket-Builds und Pinning.
Das moderne Äquivalent von add-source
Lokale und entfernte Quell-Abhängigkeiten leben jetzt in 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 Das ist strikt mächtiger als add-source: Es bewältigt mehrere lokale Pakete, exakte git-Pins und reproduzierbare Multi-Repository-Builds.
Reproduzierbares Versions-Pinning
Für die Reproduzierbarkeit, die Sandboxes nie boten, erzeugen Sie eine Freeze-Datei, die jede transitive Version festschreibt:
$ cabal freeze # writes cabal.project.freeze
# commit it — every machine now resolves the identical plan Und was ist mit Stack?
Stack geht dieselben Ziele aus einem anderen Winkel an: Statt Versionen pro Projekt aufzulösen, baut es gegen kuratierte Snapshots (die Stackage-Resolver wie lts-22.x), bei denen ein ganzer Satz von Paketen nachweislich zusammen kompiliert, und teilt Builds global pro Snapshot. Beide Werkzeuge sind 2026 solide und weit verbreitet; die praktische Aufteilung lautet grob „ich will die Standard-Toolchain und feine Kontrolle“ (Cabal) gegen „ich will ein kuratiertes, verlässliches Set schlüsselfertig“ (Stack). Keines von beiden verwendet Sandboxes — das Konzept ist auf beiden Seiten vollständig überholt.
Migrations-Spickzettel
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) Die Lektion, die Sandboxes lehrten — niemals globalen veränderlichen Zustand verschmutzen, pro Projekt isolieren — setzte sich so vollständig durch, dass sie unsichtbar wurde: Es ist einfach die Art, wie sich cabal build verhält. Wenn Sie dies lesen, weil ein alter Link oder ein Tutorial Sie auf cabal sandbox init verwiesen hat, können Sie diesen Befehl bedenkenlos vergessen. Führen Sie einfach cabal build aus.