Is `cabal2nix` the (only) recommend way to convert/create small Haskell project as of today?

I guess the same question is asked many times, ,my apologies, but I don’t sure if the current resources is up-to-date and some seem too complex for my need.

My current focus is on the standalone (typically small-sized and mostly just executables) project.

I have read the followings resources:

My process for developing new project is is usually:

  • nix-shell -p with cabal-install and ghc
  • cabal init
  • cabal new-build

Is cabal2nix the good fit for me? Should I use other way else?

4 Likes

cabal2nix converts Haskell projects to nix expressions based on the currently available package set in nixpkgs. This usually follows stackage. If your Haskell project works with this, then it is probably the most convenient way to get started. You get the benefit of using the binary cache, so you don’t even need to build much.

It quickly gets frustrating when your dependencies don’t match what is available in the default package set. You begin to start needing to override the packages to get the exact dependencies you want, and this can quickly get out of hand.

I’ve recently been playing with https://github.com/input-output-hk/haskell.nix which take the approach of conjuring up the package set you need using cabals solver (or stacks). It then builds a nix expression which matches exactly what cabal or stack would do at that point in time, and allows you to reproduce it whenever. This is much easier to work with as you can just use your normal tools to come up with the build plan, and simply use nix for caching and reproducibility (check in the generated nix files much like you would a cabal lock file).

It’s a bit young, and has some rough edges, but certainly takes what I consider to be the right approach here by delegating more work to the existing tools (cabal and stack).

3 Likes

Based on @utdemir’s comment on my tutorial announcement I came across this tweet-thread today https://twitter.com/haitlah/status/990156165534441472 .
Haven’t had the time to try it out yet.

If I understand correctly the question is actually, what do you gain by defining nix expressions for your haskell package vs. simply invoking nix-shell -p ...

nix-shell -p ... is nice for rapid prototyping, but it’s not really reproducible as you’re likely to use your user’s or system channels (this can be avoided for example by setting NIX_PATH). Another problem with it is that when you need to override packages, specifying those overrides in the arguments of nix-shell becomes a mess.

Also how do you currently persist this nix-shell -p ...-based setup in the project? bash scripts?

cabal init is how I usually create a project. Its a one-time process. After that I would use cabal2nix to generate default.nix

The reason I want to use nix with Haskell project is to be able to install and the binary/library using nix-env -i/nix-shell -p, for example, if I write a utility I want to use it in my shell anywhere.

AFAIK, I should not use cabal install/stack install to install my binaries.

If installing your package and getting a development shell are your only goals then you can simply create the files:

# default.nix
let nixpkgs = import <nixpkgs> {};
in nixpkgs.haskellPackages.callCabal2nix "<PKG_NAME>" ./. {}
# shell.nix
(import ./default.nix).env

Then you can install the package like so:

nix-env -i -f ./default.nix

And get a develepment environment (nix-shell -p ... equivalent) like this:

nix-shell

Beware that this is good only for quick hacking and it is likely to break as the haskell package set of your channel changes with time.

2 Likes

I’m working on a repository contianing shell.nix as template to bootstrap projects. I already have a haskell, but will add the nixpkgs.haskellPackages.callCabal2nix "<PKG_NAME>" ./. {} approach.