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

Haskell · Cabal · ferramentas de build

Cabal 2.0: a versão que redefiniu as builds Haskell

Por ColdwastAtualizado em 13 de junho de 20267 min de leitura#haskell#cabal#build
Um grafo de dependências e um terminal a executar cabal new-build, ilustrando as builds ao estilo nix do Cabal 2.0
O Cabal 2.0 fez do planeamento por projeto ao estilo nix o alicerce da toolchain moderna.
Reescrita atualizada, mantida pela comunidade — conteúdo original, não escrito pelo anterior proprietário do domínio nem afiliado a ele. Onde factos de 2017 mudaram desde então, o estado de 2026 é assinalado explicitamente.

O Cabal 2.0 saiu em setembro de 2017 a par do GHC 8.2 e, em retrospetiva, é a versão que definiu a orientação das ferramentas Haskell para a década seguinte. É o momento em que três coisas se tornaram reais de uma só vez: as builds locais ao estilo nix, o Backpack e um formato .cabal moderno com uma sintaxe de versão mais sã. Eis o que era cada uma e em que ponto está hoje.

1. Builds locais ao estilo nix (new-build)

A funcionalidade de bandeira. Antes, as sandboxes isolavam as dependências por projeto mas reconstruíam tudo do zero e não eram reproduzíveis. O Cabal 2.0 promoveu o cabal new-build: um modelo endereçado por conteúdo em que cada combinação única de pacote + versão + opções + dependências é construída uma só vez, armazenada sob um hash num store global partilhado, e reutilizada por qualquer projeto cujo plano de build dela precise.

$ cabal new-build      # 2017 spelling
$ cabal build          # 2026: this IS new-build — it became the default in Cabal 3.0

Estado em 2026: o prefixo new-* desapareceu. cabal build, cabal run, cabal repl e cabal test são todos ao estilo nix por predefinição. Os aliases v2- ainda existem para scripts; os antigos comandos v1- estão descontinuados. O que o Cabal 2.0 introduziu como opção é hoje simplesmente a forma como o Cabal funciona.

2. Backpack — pacotes mixin e assinaturas de módulo

O Backpack acrescentou ao Haskell uma verdadeira modularidade paramétrica: uma biblioteca pode declarar uma assinatura de módulo (uma interface) e ser tipada contra ela sem se comprometer com uma implementação concreta; um consumidor «mistura» depois a implementação que quiser. O exemplo clássico é uma biblioteca escrita contra uma assinatura abstrata Str que pode ser instanciada com String, Text strict ou ByteString sem duplicação de 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 em 2026: o Backpack funciona e continua a ser suportado, mas manteve-se uma ferramenta de especialistas em vez de um padrão generalizado — as classes de tipos e a parametrização comum cobrem a maioria das necessidades, e o suporte de ferramentas/IDE às assinaturas permaneceu limitado. Recorra a ele quando precisar mesmo de compilar o mesmo código contra vários tipos concretos; caso contrário, raramente o encontrará.

3. Um formato .cabal moderno

Definir cabal-version: 2.0 desbloqueou melhorias de formato que são hoje o quotidiano do Haskell:

  • O operador caret ^>=. build-depends: aeson ^>= 2.1 significa «>= 2.1 && < 2.2» — uma forma compacta e reveladora de intenção de respeitar o limite superior do PVP. Encontra-se por toda a parte nos pacotes modernos.
  • autogen-modules, para que módulos gerados como Paths_yourpkg sejam declarados corretamente e entregues no sdist.
  • Bibliotecas externas (a stanza foreign-library), permitindo construir código Haskell como objeto partilhado para ser consumido por outras linguagens.
  • build-depends por componente, para que a biblioteca, os testes e os benchmarks de um pacote tenham cada um o seu conjunto preciso de dependências.

Estado em 2026: tudo é padrão. A recomendação hoje é definir cabal-version: 3.0 ou mais recente (implica tudo o que o 2.0 trazia, mais as stanzas comuns do 2.2, o curinga ** para ficheiros de dados, e mais). Use 2.0 apenas se tiver de suportar uma toolchain muito antiga.

4. O solver de dependências melhorou

Código-fonte aberto num editor de código
Código-fonte aberto num editor de código

O Cabal 2.0 trouxe melhorias ao solver que o tornaram simultaneamente mais rápido e mais propenso a encontrar um plano de instalação válido num grafo de dependências profundo, com explicações mais claras quando nenhum plano existe. O solver continuou a melhorar desde então, mas foi com o 2.0 que «o solver geralmente funciona sozinho» começou a ser verdade para projetos não triviais.

Por que isso foi importante

Antes do 2.0, a história das builds Haskell era «instala globalmente e reza», remendada pelas sandboxes. Depois do 2.0, tornou-se «declara o que queres, obténs uma build reproduzível, partilhada e isolada» — o mesmo modelo mental do Nix, do Cargo ou dos gestores de pacotes modernos noutros lugares. Cada cabal build que executar em 2026 é o descendente direto daquilo que o 2.0 tornou o caminho predefinido.

Se atualizar um projeto antigo

# 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

Leia também: As sandboxes Cabal e o que as substituiu · Paralelizar o cabal-install · todos os guias