Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

nix-shell as #! interpreter for runghc

Tags:

haskell

nix

I'm trying to use nix-shell as a #! wrapper for runghc, as suggested in the manpage. But it cannot find the libraries. Given the following example, cut-down from the manpage

#! /usr/bin/env nix-shell
#! nix-shell -i runghc -p haskellPackages.HTTP

import Network.HTTP

main = return ()

I get the following result:

[~:0]$ ./nixshelltest 

nixshelltest:4:1: error:
    Failed to load interface for ‘Network.HTTP’
    Use -v to see a list of the files searched for.
[~:1]$ 

to my mind, that's exactly what nix-shell -p is to avoid.

Am I doing something wrong, missing the point, or is there a bug? This is both on a nixOS 17.03 host, and also a host running nix 17.09 on top of Ubuntu.

Thanks,

like image 729
user3416536 Avatar asked Jul 10 '17 21:07

user3416536


People also ask

What is nix-shell for?

Description. The command nix-shell will build the dependencies of the specified derivation, but not the derivation itself. It will then start an interactive shell in which all environment variables defined by the derivation path have been set to their corresponding values, and the script $stdenv/setup has been sourced.

Is nix same as Linux?

NixOS is a Linux distribution using Nix for managing software in the system, including the Linux kernel.

What are nix flakes?

A flake is (usually) a Git repository that contains a file named flake. nix at top-level. Flakes provide an attribute set of values, such as packages, Nixpkgs overlays, NixOS modules, library functions, Hydra jobs, nix-shell definitions, etc. Flakes can depend on other flakes.

Why should you use NixOS?

NixOS makes it easier to keep custom packages as part of your OS configuration. Rather than writing custom, imperative install scripts to run when setting up your machine, you can simply list your custom packages in your system's package list, and they will be built automatically.


1 Answers

The environment that you're using to run the script is missing a step. It's got a GHC and a HTTP package, but the compiler doesn't know about the package.

The way GHC and library packages work in nix might be a little "inside out" from what you're expecting. You actually need to install a compiler that "depends on" all of the libraries you want, rather than simply installing the compiler and the library separately. The reason is that GHC is designed to have library packages added by modifying the file tree where GHC is installed. On a mutable file system with only a single system GHC install you would just modify GHC whenever a library was installed, but nix can't. Nix has to be able to install a frozen GHC that never changes, and potentially many of them.

So what happens instead is that you install a tiny wrapper which depends on the both the underlying "raw" GHC install and all of the libraries that you want to use. The wrapper then acts like an install of GHC that had those libraries registered, without actually needing to duplicate an entire GHC install. When you just install a library package on its own it just sits there inert, without any GHC being able to find it just by it existing.

In fact, the script you've shown here doesn't actually specify that it should have a compiler installed at all; it just asks for the HTTP library. When I tried your script I got command not found: runghc. The runghc is only working on your system because it happened to already be in your path when you ran this (perhaps because you have GHC installed in your profile?), and that GHC wasn't installed with the HTTP package and so can't see it. The nix-shell adding just the library to the environment doesn't help.

What you need to do instead is use this line:

#! nix-shell -i runghc -p "ghc.withPackages (ps: [ ps.HTTP ])"

You're not installing either ghc or HTTP directly; instead the ghc.withPackages function computes a nix package that installs a GHC wrapper that knows about the HTTP Haskell package. Under the hood this depends on a "raw" GHC with no additional libraries, and also on the HTTP library and will cause those to be installed too.

If you use lots of different Haskell environments (possibly via nix-shell scripts like this that each need a different set of libraries), then you will end up with a unique withPackages wrapper installed on your system for each combination of libraries you ever use. But that's okay because the wrappers themselves are tiny, and nix is able to share and reuse the underlying GHCs and library packages between all of those environments.

like image 125
Ben Avatar answered Sep 28 '22 06:09

Ben