• Stars
    star
    102
  • Rank 335,584 (Top 7 %)
  • Language
    Nix
  • License
    MIT License
  • Created over 5 years ago
  • Updated over 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A Haskell project template that uses Nix and comes with cabal-install, ghcid, ormolu, haskell-language-server and more.

hs-nix-template

CI Status

A cookiecutter template which creates a Haskell project that

  • Can be built with Nix and cabal-install,
  • Has a library, an executable and a test suite,
  • Comes with a shell.nix which provides an environment with haskell-language-server, ghcid, ormolu and more,
  • ghci integrates with Haddock and Hoogle for all dependencies (:doc, :hoogle),
  • Can be built into a Docker container,
  • Uses a pinned nixpkgs managed by niv.

Usage

No need to install anything, just run:

nix-shell -p cookiecutter git --run 'cookiecutter gh:utdemir/hs-nix-template'

Once that completes, cd into the directory and call:

nix-shell

(includes a recent GHC, cabal, a populated hoogle database, haskell-language-server, ghcid and more)

Or you can directly build the executable for your project with:

nix-build --attr exe

Or deploy to docker image:

nix-build --attr docker

And load the resulting image:

docker load -i result

Cheat Sheet

Use the hpack configuration tool

When running cookiecutter, you'll be presented with the option of using cabal's default configuration format or hpack. When using hpack, you will need to run hpack to generate a cabal file before building, i.e.

hpack .
cabal build

The nix shell will contain hpack for local development and the nix build will automatically handle changes to package.yaml. See the hpack documentation for more information.

Run Hoogle locally

  • hoogle server --local -p 3000 -n

You need the -n to stop hoogle from trying to use https locally. You will need to kill and reload this whenever you add or remove a dependency in your cabal file (if you use lorri your shell will reload for you as well). This is so hoogle will use the newly generated database with your added/modified dependencies.

Add external source with niv

  • From Hackage
    • niv add extra --version 1.6.20 -a name=extra -t 'https://hackage.haskell.org/package/<name>-<version>/<name>-<version>.tar.gz'
  • From GitHub with specific revision
    • niv add ndmitchell/ghcid -a rev=e3a65cd986805948687d9450717efe00ff01e3b5

Add external tool to default.nix

The sources you pull down with niv are accessible under sources.<name> (fyi: <name> is the key in sources.json). However, that is a derivation with the attributes you need to fetch the source, not the source itself. If you want to pull in a source you need to either give it to a function that knows how to fetch the contents like callCabal2nix or if the source has a default.nix you can import it directly, like so: import sources.<name> {}.


Often you will want to explore what you have just imported since you may only want one of its attributes, you can do this by adding the source as an exported attribute in your default.nix:

in
  if pkgs.lib.inNixShell then shell else {
    inherit exe;
    inherit docker;
    ### Added here
    src = import sources.<name> {};
  }

Then call nix repl default.nix and you can tab complete src.<tab> to see what attributes are inside. (Note, this is how I know to call (import sources.niv {}).niv to get the niv derivation).


On the other hand here is an example with callCabal2nix adding the specific version of ghcid we fetched earlier to our development shell:

    buildInputs = with pkgs.haskellPackages; [
      cabal-install
      ### Added modified development tool here
      (pkgs.haskell.lib.justStaticExecutables
          (pkgs.haskellPackages.callCabal2nix "ghcid" (sources.ghcid) {}))
      ormolu
      hlint
      pkgs.niv
      pkgs.nixpkgs-fmt
    ];

If you exit your nix-shell to reload this change you will find it won't build. However, this means you won't have access to niv or other development tools you may need to get the derivation building again. I strongly recommend using lorri to handle re-building your development environment, among other useful features it will load up the last successful build for your development environment alleviating this issue entirely.

To get this version of ghcid building you need to provide a specific version of the extra library:

  extra = pkgs.haskellPackages.callCabal2nix "extra" (sources.extra) {};

  shell = myHaskellPackages.shellFor {
    packages = p: [

Then add it to the end of your callCabal2nix call:

      (pkgs.haskell.lib.justStaticExecutables
          (pkgs.haskellPackages.callCabal2nix "ghcid" (sources.ghcid) {inherit extra;}))

Note: I am building ghcid with haskellPackages not myHaskellPackages. If the tool fails to build you might want to either use a different package set or modify one yourself so the tool has the right dependencies.

Speed up dependency building

For some packages, like extra we don't need its documentation or setup for profiling since its just a dependency of a build tool. You can speed up building dependencies with a modified package set:

  fastHaskellPackages = pkgs.haskellPackages.override {
    overrides = hself: hsuper: rec {
      mkDerivation = args: hsuper.mkDerivation (args // {
        doCheck = false;
        doHaddock = false;
        enableLibraryProfiling = false;
        enableExecutableProfiling = false;
        jailbreak = true;
      });
    };
  };

  ### The external library then is build with the modified package set
  extra = fastHaskellPackages.callCabal2nix "extra" (sources.extra) {};

Add external dependency to default.nix

Lets say you wanted to add extra as dependency of your project and its not in the package set by default:

  myHaskellPackages = pkgs.haskell.packages.${compiler}.override {
    overrides = hself: hsuper: {
      ### Add new dependences here
      extra =
        hself.callCabal2nix
          "extra"
          (sources.extra)
          {};
      ### Local package without a default.nix and don't run tests
      hedgehog = self.haskell.lib.dontCheck (hself.callCabal2nix "hedgehog" /absolute/path/to/project/haskell-hedgehog/hedgehog {});
      "your-project-name" =
        hself.callCabal2nix
          "your-project-name"
          (gitignore ./.)
          {};
    };
  };

This will not only add extra to your project, but also build the documentation for you. However, to get it in your local hoogle database you need to add it to your cabal file and then call nix-shell.

Override dependency in default.nix

If you want to override a dependency you add it like we did above with extra, just make sure the name is identical to what is in the package set. As you would expect, the name in the package set is the same as the name on Hackage. However, there are a few packages with multiple versions, like zip and zip_1_4_1.

If you want to see exactly what is in your modified package run nix repl default.nix and you will get this:


Loading 'default.nix'...
Added 3 variables.

nix-repl>

Then you can tab complete to see what is in myHaskellPackages

nix-repl> myHaskellPackages.ex<tab>

This is also the best way to find out what versions of libraries are in a package set. Instead of having to add them to your cabal file to find out the version you can just view the version attribute. Again in nix repl

nix-repl> myHaskellPackages.extra.version
1.6.20

Importing your library in another project

One of the attributes default.nix exports is "your-project-name". This is so you can easily import your project's library into your other Haskell projects.

If you want to import your project locally you can just directly reference the default.nix file.

  myHaskellPackages = pkgs.haskell.packages.${compiler}.override {
    overrides = hself: hsuper: {
      ### local import
      "your-project-name" =
        (import /absolute/path/to/your-project-name/default.nix {}).your-project-name;

The downside to this approach is that your continuous integration or others won't be able to build your project from scratch. You need to host the code somewhere online and fetch it in the derivation. You can use niv to fetch your code from github or the like and then import it like so:

  myHaskellPackages = pkgs.haskell.packages.${compiler}.override {
    overrides = hself: hsuper: {
      ### local import
      "your-project-name" =
        (import source.your-project-name {}).your-project-name;

Deploy to Docker Image

The third project in haskell-nix goes into detail how this works, but we have already included docker under the docker attribute.

Note: if your project name has a space in it, the executable path will be wrong.

More Repositories

1

nix-tree

Interactively browse dependency graphs of Nix derivations.
Haskell
632
star
2

ghc-musl

Docker image with GHC+musl for static executables
Shell
135
star
3

distributed-dataset

A distributed data processing framework in Haskell.
Haskell
114
star
4

dotfiles-nix

Comprehensive configurations of my NixOS workstations and home server.
Nix
84
star
5

nixlisp

Nix
59
star
6

bencoder

A simple bencode decoder-encoder library in pure Python.
Python
32
star
7

qualified-imports-plugin

Haskell
16
star
8

composable-indexes

Index arbitrary JavaScript objects with multiple dimensions.
TypeScript
9
star
9

hs-pivotal-tracker

A Haskell library and a CLI tool for interacting with Pivotal Tracker
Haskell
5
star
10

recursive-let-plugin

An experiment to implement something similar to RecursiveLet proposal using GHC plugins.
Haskell
4
star
11

zsh-up

ZSH integration for the Ultimate Plumber
Shell
4
star
12

utdemir.com

Source and build scripts for my personal website.
JavaScript
4
star
13

handsy

[DEPRECATED] A Haskell DSL to describe common shell operations and interpeters for running them locally and remotely.
Haskell
4
star
14

bar

Configurable progress bars/status monitors for Python console applications.
Python
4
star
15

midye

Haskell
4
star
16

emacs-with-config

A Nix function for customizing Emacs
Emacs Lisp
4
star
17

thlpe

Haskell implementation of "The Hardest Logic Puzzle Ever"
Haskell
2
star
18

hamza

Haskell
2
star
19

dotfiles

Dotfiles for my Macbook
Shell
2
star
20

qrpush

Simple application for transferring files to a smart phone.
Ruby
2
star
21

talks

Nix
2
star
22

serverless-hs

A Haskell web framework that runs on AWS Lambda. Currently incomplete, large parts are missing, nothing is working.
Haskell
2
star
23

aoc2022

Haskell
1
star
24

network-transport-websockets

A websocket transport implementing network-transport API
Haskell
1
star
25

trying-yi

Haskell
1
star
26

allrgb

Scala
1
star
27

lmdb-safe

A bit safer lmdb binding for Haskell.
Haskell
1
star
28

apidoc-hs

Generate Haskell data types from Apidoc schemas using Template Haskell.
Haskell
1
star
29

gcal-i-am-busy

Nix
1
star
30

furby

A simplified rawdog clone, in Ruby
HTML
1
star
31

cookiecutter-haskell

Nix
1
star