Search code examples
haskellnixos

Running shell commands from Haskell in NixOS


I'm fairly new to NixOS, and am trying to invoke emacs from a Haskell program using the following function:

ediff :: String -> String -> String -> IO ()
ediff testName a b = do
  a' <- writeSystemTempFile (testName ++ ".expected") a
  b' <- writeSystemTempFile (testName ++ ".received") b
  let quote s = "\"" ++ s ++ "\""
  callCommand $ "emacs --eval \'(ediff-files " ++ quote a' ++ quote b' ++ ")\'"

When I run the program that invokes this command using stack test, I get the following result (interspersed with unit test results):

/bin/sh: emacs: command not found
Exception: callCommand: emacs --eval '(ediff-files "/run/user/1000/ast1780695788709393584.expected" "/run/user/1000/ast4917054031918502651.received")'

When I run the command that failed to run above from my shell, it works flawlessly. How can I run processes from Haskell in NixOS, as though I had invoked them directly, so that they can access the same commands and configurations as my user?


Solution

  • Both your shell and callCommand use the PATH environment variable, so it seems like stack is changing that. It turns out that stack uses a pure nix shell by default, but you also want to access your user environment, which is 'impure'.

    To quote the stack documenation

    By default, stack will run the build in a pure Nix build environment (or shell), which means the build should fail if you haven't specified all the dependencies in the packages: section of the stack.yaml file, even if these dependencies are installed elsewhere on your system. This behaviour enforces a complete description of the build environment to facilitate reproducibility. To override this behaviour, add pure: false to your stack.yaml or pass the --no-nix-pure option to the command line.

    Another solution is to add Emacs to nix.dependencies in stack.yaml (thanks @chepner). It has the benefit that some version of Emacs will always be available when a developer runs the tests, but that Emacs may not be the Emacs they want to use. You may be able to work around that using something like ~/.config/nixpkgs/config.nix, unless they have configured their Emacs elsewhere, like the system configuration or perhaps a home manager. I'd prefer the simple but impure $PATH solution.