3 ms·
Nix has no dependency resolution: all dependencies are specified exactly for every package. The reason this is tractable is that Nix packages are written in an
by chriswarbo 10y ago
Nix has no dependency resolution: all dependencies are specified exactly for every package.
The reason this is tractable is that Nix packages are written in an expressive language. Most package definitions are actually functions, which accept their dependencies as arguments. Such functions provide flexibility, making it easy to switch out dependencies or perform other changes, and are often passed around using higher-order functions. The closest thing to dependency resolution is to take the fixed-point of a function, which is used to populate each package's dependency arguments using the other packages (which we're also populating the arguments of; this relies on Nix being lazy!).
For example, the `cabal2nix` tool can read a Haskell '.cabal' file and create a Nix function, which accepts dependencies and returns a package. We can turn this into a concrete package by specifying a version of Haskell to use (e.g. `haskell.ghc7103` for GHC 7.10.3, `haskell.ghc801` for GHC 8.0.1, `haskell.ghcjs` for GHCJS, etc.); each contains a higher-order function `callPackage`, which we pass our generated function to, along with any non-default overrides.
For example, we can run the command:
nix-shell -p "(haskell.ghc7103.callPackage ($(cabal2nix /path/to/project.cabal)) { some-dep = my-version-of-some-dep; })"
This will drop us into a bash shell where the Haskell project at /path/to/project.cabal has been built using GHC 7.10.3, with default versions of all dependencies except for "some-dep" which we override to be some other package. If, for example, our Haskell package defines a bunch of executables, they'll be available in this shell's $PATH.
There are also higher-order functions like `ghcWithPackages` for defining Haskell environments with pre-populated package databases, `override` which takes the fixed-point of a set of packages (so overriding "foo" ensures that all packages depending on "foo" get our version, transitively), etc.