Haskell · Cabal · ferramentas de build
As sandboxes Cabal: o que eram e o que as substituiu
cabal sandbox pertence agora à história — se quiser apenas o fluxo de trabalho moderno, salte diretamente para o que usar hoje.Quando o cabal sandbox surgiu no cabal-install 1.18 em agosto de 2013, corrigiu a coisa mais incómoda do desenvolvimento Haskell da época: a base de dados global de pacotes. Antes das sandboxes, cada cabal install escrevia num único store ~/.cabal partilhado. Instale dois projetos que exijam versões diferentes da mesma biblioteca e obtinha o famigerado «inferno das dependências» — uma base global corrompida que muitas vezes terminava com rm -rf ~/.ghc ~/.cabal e um recomeço.
As sandboxes tornaram as dependências específicas de cada projeto. Esse princípio nunca desapareceu — mas o mecanismo sim. Eis o quadro completo, do funcionamento das sandboxes ao fluxo de trabalho ao estilo nix que as substituiu.
O que era realmente uma sandbox
Uma sandbox era uma base de dados de pacotes local ao projeto. Criava uma na pasta do seu projeto:
$ 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 daí, o cabal install e o cabal build executados nessa pasta usavam .cabal-sandbox/ em vez do store global. Dois projetos podiam depender de versões em conflito de aeson ou text sem nunca interferirem, porque cada um tinha a sua própria base isolada.
Dependências de fonte locais
A outra funcionalidade verdadeiramente útil era add-source, que permitia a uma sandbox incluir uma biblioteca diretamente a partir de um repositório local — inestimável quando se mexe numa biblioteca e numa aplicação em conjunto:
$ cabal sandbox add-source ../my-library
$ cabal install --dependencies-only
$ cabal build Modificava ../my-library, reconstruía a aplicação, e o cabal detetava a alteração e recompilava a dependência. Era o mais próximo de um fluxo de trabalho monorepo que o Haskell tinha em 2013.
Porque as sandboxes acabaram por ser removidas
As sandboxes resolviam o isolamento mas deixavam dois problemas por resolver:
- Nenhuma partilha entre projetos. Cada sandbox recompilava o mundo do zero. Dez projetos a depender de
lenssignificavam dez builds distintas delense da sua enorme árvore de dependências — lento e voraz em disco. - Nenhuma reprodutibilidade entre máquinas. Uma sandbox capturava onde os pacotes viviam, não um plano preciso e reproduzível de quais versões exatas seriam escolhidas. Dois programadores a executar os mesmos comandos com uma semana de intervalo podiam ainda assim obter builds diferentes.
A solução, emprestada conceptualmente do Nix, foi tornar as builds endereçadas por conteúdo e partilhadas globalmente: construir uma dada combinação pacote-versão-opções uma só vez, armazená-la sob um hash num store global imutável, e deixar que cada projeto que precise dessa combinação exata a reutilize. São as «builds locais ao estilo nix», surgidas com o nome new-build no Cabal 2.0.
O que usar hoje (2026)
O cabal sandbox foi descontinuado durante a série Cabal 2.x e removido por completo nas versões posteriores. O moderno fluxo de trabalho predefinido do atual cabal-install não requer nenhum passo de init — o isolamento é 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 O que acontece sob o capô é exatamente o isolamento por projeto que as sandboxes prometiam, mais a partilha que lhes faltava:
- Os artefactos de build do seu projeto vão para uma pasta local
dist-newstyle/(o sucessor espiritual de.cabal-sandbox/). - As dependências compiladas vão para um store global imutável (
~/.cabal/store, ou sob~/.local/state/cabalnos esquemas recentes), indexado por um hash do pacote, versão, dependências e opções. Construalensuma vez; cada projeto que reutilize o mesmo plano referencia a mesma entrada do store. - Um ficheiro
cabal.projectsubstitui ocabal.sandbox.configpara configuração, builds multipacote e pinning.
O equivalente moderno de add-source
As dependências de fonte locais e remotas vivem agora em 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 É estritamente mais poderoso do que add-source: lida com vários pacotes locais, pins git exatos e builds multirrepositório reproduzíveis.
Pinning de versões reproduzível
Para a reprodutibilidade que as sandboxes nunca ofereceram, gere um ficheiro de freeze que fixa cada versão transitiva:
$ cabal freeze # writes cabal.project.freeze
# commit it — every machine now resolves the identical plan E o Stack nisto tudo?
O Stack aborda os mesmos objetivos de outro ângulo: em vez de resolver versões por projeto, constrói contra snapshots curados (os resolvers Stackage como lts-22.x) onde se sabe que um conjunto inteiro de pacotes compila em conjunto, e partilha builds globalmente por snapshot. Ambas as ferramentas são sólidas e amplamente usadas em 2026; a divisão prática é, grosso modo, «quero a toolchain padrão e controlo fino» (Cabal) contra «quero um conjunto curado e fiável chave na mão» (Stack). Nenhuma usa sandboxes — o conceito está plenamente ultrapassado de ambos os lados.
Memorando de migração
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) A lição que as sandboxes ensinaram — nunca poluir um estado mutável global, isolar por projeto — prevaleceu de forma tão completa que se tornou invisível: é simplesmente a forma como o cabal build se comporta. Se está a ler isto porque um link ou tutorial antigo o apontou para cabal sandbox init, pode esquecer esse comando sem receio. Basta executar cabal build.