coldwa.st
Todos os guiasProgramaçãoWebDadosFerramentasBases de dadosHaskellConceitosCabal e buildsToolchainCompiladorDesempenhoEditor e HLS

Haskell · Cabal · ferramentas de build

As sandboxes Cabal: o que eram e o que as substituiu

Por ColdwastAtualizado em 13 de junho de 20269 min de leitura#haskell#cabal#build
Três sandboxes Cabal isoladas, cada uma com o seu próprio conjunto de pacotes, ao lado de um terminal a executar cabal sandbox init
O isolamento de dependências por projeto — a ideia que as sandboxes introduziram, hoje o comportamento predefinido.
Esta é uma reescrita atualizada, mantida pela comunidade, de um tema pelo qual este domínio foi historicamente conhecido. É conteúdo original, não escrito pelo anterior proprietário do domínio nem afiliado a ele. O próprio 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 lens significavam dez builds distintas de lens e 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)

Código-fonte num ecrã de computador
Código-fonte num ecrã de computador

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/cabal nos esquemas recentes), indexado por um hash do pacote, versão, dependências e opções. Construa lens uma vez; cada projeto que reutilize o mesmo plano referencia a mesma entrada do store.
  • Um ficheiro cabal.project substitui o cabal.sandbox.config para 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.


Leia também: Cabal 2.0 e as builds ao estilo nix · Paralelizar o cabal-install · todos os guias