Adding to your project

Adding Shpadoinkle to a project should be easy.

See an example of this working by examining nix/base.nix in the Seed Project. You can add packages from Hackage and standard Haskell toolchain.

GHCjs Challenges

Introducing GHCjs brings a host of problems — many dependencies require low-level C headers, some require specific outdated versions (or worse yet, forks) of packages, and some just need redesign in order to be practical in a JavaScript environment. All of these issues are solved with Nix; it lets us "overlay" dependencies with specific versions we require, while being transparent and compatible to the end-user.

For instance, there are other obstacles introduced by testing libraries — packages that should work just fine, but because they rely on low-level interfaces, they will fail to build with GHCjs. As another example, many Haskell packages use a library called doctest for verifying example code in documentation — this is not compatible with GHCjs, and will prevent any dependencies requiring it from building.

If that weren’t bad enough, the stock GHCjs and the GHCjs package set is not optimized. However, this has been remediated in the Reflex Platform, which supports Nix. These optimizations require some advanced requirements — custom compilation of the GHCjs compiler, as well as many downstream packages this project depends on.

All of these issues are addressed by building Nix package overlays.

Overlays provide a method to extend and change nixpkgs. They replace constructs like packageOverride and overridePackages.

By exposing overlays, we allow you to compose these necessary changes into your nixpkgs set for your existing project:

shpadoinkle = builtins.fetchGit { (1)
  url    = https://gitlab.com/platonic/shpadoinkle.git;
  rev    = "a8e979f62d25bf39cbeb008bef98364dfbd2d227";
};

shpadoinkle-overlay = (2)
  import (shpadoinkle + "/nix/overlay.nix") { compiler = "ghc863"; isJS = false; };

pkgs = import
  (builtins.fetchTarball { url = "https://github.com/nixos/nixpkgs/archive/5272327b81ed355bbed5659b8d303cf2979b6953.tar.gz"; })
  { overlays = [ shpadoinkle-overlay my-overlay ]; }; (3)
1 Get the Shpadoinkle source code.
2 This overlay makes dependencies compatible with Shpadoinkle targets as well as adds in optimizations of GHCjs from Reflex Platform.
3 Compose the overlays onto your package set.

Extending Nix Haskell

Extending the Haskell package sets in Nix has some pitfalls:

No Implicit GHC Version

These overlays target a specific compiler version — they’re applied to targets such as pkgs.haskell.packages.ghc863, but not applied to version-ambiguous targets like pkgs.haskellPackages. If you modify haskellPackages in your overlay, those changes won’t be reflected in the targets we use in our overlays. You might expect that if the compiler version targeted is the same as the implicit version in haskellPackages, and modifications would be kept in sync — this is not the case.

Compose Extensions

If overlays modifying Haskell package sets do not use composeExtensions, new overlays will simply replace previous ones, instead of composing them together. Follow this pattern and everything will be A-OK:

self: super:
let
  shpadoinkle = fetchgit { (1)
    url    = https://gitlab.com/platonic/shpadoinkle.git;
    rev    = "a8e979f62d25bf39cbeb008bef98364dfbd2d227";
  };
  haskell-overlay = hself: hsuper: { (2)
    Shpadoinkle      = hself.callCabal2nix "Shpadoinkle"      (shpadoinkle + "/core");
    Shpadoinkle-html = hself.callCabal2nix "Shpadoinkle-html" (shpadoinkle + "/html");
  };
in {
haskell = super.haskell //
  { packages = super.haskell.packages //
    { ghc863 = super.haskell.packages.ghc863.override (old: { (3)
        overrides = super.lib.composeExtensions (4)
          (old.overrides or (_: _: {})) haskell-overlay;
      });
    };
  };
}
1 Here for illustrative reasons
2 Add packages in your application’s Haskell overlay.
3 Override a specific compiler version, not haskellPackages.
4 Use composeExtensions to preserve work done in earlier overlays