Haskell · Cabal · ferramentas de build
Cabal 2.0: a versão que redefiniu as builds Haskell
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.1significa «>= 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 comoPaths_yourpkgsejam 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-dependspor 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
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