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.hsthin and everything testable. build-dependslists packages with version bounds.- Every module must be listed in
exposed-modules(orother-modules). default-language: GHC2021turns 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 findstakeanddrop.
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.