Skip to content
AgileLogics
7 min read

Cabal and Stack Projects

Prerequisites: Modules

Single files are fine for learning. Real programs are packages, built with cabal (or Stack), which fetch dependencies from Hackage, compile your modules in the right order, and run tests.

Creating a project

mkdir todo-app
cd todo-app
cabal init --non-interactive --exe --tests

That creates a layout like this:

todo-app/
├── todo-app.cabal     # package description
├── app/Main.hs        # executable entry point
├── src/               # library modules (if you add a library)
├── test/Main.hs       # test suite
└── CHANGELOG.md

The .cabal file

cabal-version:      3.0
name:               todo-app
version:            0.1.0.0

common warnings
    ghc-options: -Wall

library
    import:           warnings
    exposed-modules:  Todo.Core, Todo.Storage
    build-depends:    base >=4.18 && <5, containers, text
    hs-source-dirs:   src
    default-language: GHC2021

executable todo-app
    import:           warnings
    main-is:          Main.hs
    build-depends:    base, todo-app
    hs-source-dirs:   app
    default-language: GHC2021

test-suite todo-app-test
    import:           warnings
    type:             exitcode-stdio-1.0
    main-is:          Main.hs
    build-depends:    base, todo-app, hspec
    hs-source-dirs:   test
    default-language: GHC2021
  • Put most code in the library; the executable and tests depend on it. That keeps Main.hs thin and everything testable.
  • build-depends lists packages with version bounds.
  • Every module must be listed in exposed-modules (or other-modules).
  • default-language: GHC2021 turns on a set of widely used, stable extensions.

Everyday commands

cabal update          # refresh the Hackage package index
cabal build           # compile everything
cabal run todo-app -- add "buy milk"   # arguments after --
cabal repl            # GHCi with your library loaded
cabal test            # run the test suites
cabal install         # install the executable to ~/.local/bin (or %APPDATA%\cabal\bin)
cabal freeze          # pin exact dependency versions

cabal builds each dependency once and caches it in a global store, so different projects can share them without conflicts.

Stack

Stack is an alternative build tool built on the same Cabal library. It uses Stackage snapshots — curated sets of package versions known to build together:

stack new todo-app
stack build
stack run
stack test

Stack reads a stack.yaml (which names the Stackage snapshot to use) and usually a package.yaml that generates the .cabal file. Both tools are widely used; cabal is the default in GHCup and most new tutorials, while Stack's snapshots are popular in companies wanting reproducible builds. (cabal gets similar reproducibility from cabal freeze or a Stackage cabal.project.freeze file.)

Finding packages

  • hackage.haskell.org — every published package and its API docs.
  • Hoogle (hoogle.haskell.org) — search functions by type. Type [a] -> Int -> [a] and it finds take and drop.

What's next

Try the Intermediate Projects, then continue to Level 3 — Advanced: testing, monad transformers, concurrency, performance, and real-world libraries for JSON, parsing, and web APIs.