This is an opinionated template for creating Haskell projects. It uses Nix Flakes, hpack and cabal.
- Nix flake-based dev/CI shells with a pinned toolchain (nixpkgs + flake-parts).
- hpack-driven Cabal setup (
package.yaml->*.cabal). cabal-verifyfor a full verify pass: format, lint, build, tests, docs.- Static executable builds for Nix (
justStaticExecutables) plus a Docker image. build-static.shfor producing a musl-linked static binary via Stack.- Preconfigured formatting and linting tools (fourmolu, hlint, weeder, stan, nixfmt, statix, shfmt, shellcheck, taplo, prettier).
- Template bootstrap script (
run-template.sh) to rename and configure the project.
Create your repository from this template, clone it on your computer and enter its directory.
Then run the following to configure your project:
bash ./run-template.shIt will prompt some questions and configure your project according to your answers.
Once it is configured, provision direnv. You can copy the .envrc.tmpl:
cp .envrc.tmpl .envrcThen, you can run the following command to allow direnv to activate the
development environment:
direnv allowAlternatively, you can simply run the following command to activate the development environment:
nix developFinally, you can remove the run-template.sh script:
rm run-template.shTo run checks, linters, tests and build the codebase in the development environment, run:
cabal-verifyYou can pass -c (or --clean) to clean the build artifacts first:
cabal-verify -cAs of Cabal 3.12, you can now run the above as an external cabal command:
cabal verify [-c|--clean]cabal-verify is the "all checks" entrypoint. It runs, in order:
hpackto regenerate the.cabalfile.- Format/lint for Nix, shell, TOML, Markdown/JSON, and Haskell.
cabal build, a basiccabal run(with--version), andcabal test.weederfor dead code,stanfor static analysis, andcabal haddock.
The script stops on the first failure and prints the captured output to keep CI logs readable.
For a portable, fully static binary (musl-linked), run:
./build-static.shThis script uses Docker, Stack, and ghc-musl, then compresses the binary
with upx and copies it to /tmp/<exe>-static-<os>-<arch>.
We keep a stack.yaml because Stack is the most reliable way to build a static
musl binary without Nix. The resolver should match the nixpkgs baseline Haskell
package set (and thus the Stackage LTS used by nixpkgs) to avoid GHC or
dependency mismatches between Nix and the static build pipeline.