Skip to content

Instantly share code, notes, and snippets.

@monadplus
Last active February 10, 2023 19:46
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save monadplus/4bd19ccf19681e82d013c290fd21e663 to your computer and use it in GitHub Desktop.
Save monadplus/4bd19ccf19681e82d013c290fd21e663 to your computer and use it in GitHub Desktop.
Running a haskell script without GHC using nix

Updated version can be found on my website.

Running a haskell script without GHC

Given the following haskell script generate-random-samples.hs that requires mwc-random ...

{-# LANGUAGE ScopedTypeVariables #-}

import System.Random.MWC
import Data.Vector.Unboxed
import Control.Monad.ST

main = do
  vs <- withSystemRandom $
        \(gen::GenST s) -> uniformVector gen 20 :: ST s (Vector Int)
  print vs

... how do you run it without having to globally install the package or having to build a whole cabal project ?


One of the simplest approaches using nix is the following:

$ nix-shell --packages 'haskellPackages.ghcWithHoogle (pkgs: with pkgs; [ mwc-random ])'
nix-shell> runghc generate-random-samples.hs

In order to reuse the command so other people can run the script, you can add the following shell.nix:

{ compiler ? "ghc881" }:
let

  pkgs = import (builtins.fetchGit {
    url = "https://github.com/NixOS/nixpkgs.git";
    rev = "890440b0273caab258b38c0873c3fe758318bb4f";
    ref = "master";
  }) {};

  ghc = pkgs.haskell.packages.${compiler}.ghcWithPackages (ps: with ps; [
          mwc-random
        ]);

in

pkgs.stdenv.mkDerivation {
  name = "ghc-env";
  buildInputs = [ ghc pkgs.cabal-install ];
  shellHook = "eval $(egrep ^export ${ghc}/bin/ghc)";
}

So now you only need to call $ nix-shell to enter into a pure shell with a specific GHC version that includes all your dependencies:

$ nix-shell
nix-shell> runghc generate-random-samples.hs

The only issue is that you must be aware of how nix work in order to be able to run the script.

But these could be solved using bash shebangs in your haskell script:

#!/usr/bin/env nix-shell
#!nix-shell -i runghc
{-# LANGUAGE ScopedTypeVariables #-}

import System.Random.MWC
import Data.Vector.Unboxed
import Control.Monad.ST

main = do
  vs <- withSystemRandom $
        \(gen::GenST s) -> uniformVector gen 20 :: ST s (Vector Int)
  print vs

So now, you can run your haskell script in an environment without ghc:

./generate-random-samples.hs
[6052359640365008112,3693984866634705670,6521947999724514858,640433474764908030,-4262896110044960033,-1795671341099353119,-2220462704949887998,-248182841640258167,709016591698961687,-3622504171575206589,5987258113070378446,-159251391303273987,-8449937247808153766,6165509553180365166,-8199532339362621783,-9187765480154042269,-2389922548196927048,-4842141643835297495,-1106748185069026877,826927505518387091]
@monadplus
Copy link
Author

You can also inline everything by replacing the header by this one

!/usr/bin/env nix-shell
! nix-shell -p "haskellPackages.ghcWithPackages (pkgs: with pkgs; [ mwc-random ])" -i runghc

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment