Imports and callPackage

Import

import is one of the few keywords in nix. It allows for a file to be read and evaluated. If a directory is passed to import, then it will assume <directory>/default.nix was the desired file.

$ cat data.nix
{ a = "foo"; b = "bar"; }

nix-repl> :p import ./data.nix
{ a = "foo"; b = "bar"; }

$ cat expression.nix
5+2

nix-repl> :p import ./expression.nix
7

This still extends to functions:

$ cat function.nix
{ x, y }: x + y

nix-repl> :p import ./function.nix { x = 2; y = 9; }
11

Imports for packages

In nixpkgs, each package usually has a corresponding file associated with the packaging and related concerns of just that package. Early in nix's history, import was used to integrate the files with other expressions and allow for greater organization of code. However, the import model is quite explicit, and requires users to declare the dependencies twice.

Below is an example expression for openssl:

# pkgs/libraries/openssl/default.nix
{ lib, stdenv, fetchurl, perl }:

stdenv.mkDerivation {
  ...
}

In the import paradigm, the calling site would look:

  openssl = import ../libraries/openssl {
    inherit lib stdenv fetchurl perl;
  };

Obviously this isn't ideal. The dependencies need to be referred to three times, once at the call site, as inputs to the expression, and then within the expression at the appropriate section. The tediousness of passing the values will be solved by callPackage.

CallPackage

callPackage is a function which will call a function with the appropriate dependencies. The package set will generally expose a callPackage function with the current package set already bound.

A minimal callPackage implementation can be thought of as:

  # <nixpkgs>/lib/customisation.nix
  # callPackageWith :: Attr Set -> (Attr Set -> drv) -> Attr Set -> drv
  callPackageWith = autoArgs: fn: args:
  # autoArgs - Attr set of "defaults", for nixpkgs this would be all top-level packages
  # fn       - A nix expression which uses an attr set as in input.
  # args     - Overrides to the defaults in autoArgs
  let
    # if a file is passed, import it
    f = if lib.isFunction fn then fn else import fn;

    # find what attrs are shared from expression and package set
    # then override the values by anything passed explicitly through args
    fargs = builtins.intersectAttrs (lib.functionArgs f) autoArgs // args;
  in
    f fargs; # With nix, creation of a derivation is just function application

Usage of callPackage would look something like this:

  # <nixpkgs>/pkgs/top-level/all-packages.nix
  { lib, ... }:

  let
    self = with self; {
      ...

      callPackage = lib.callPackageWith self;

      openssl = callPackage ../libraries/openssl { };
    };
  in self

With callPackage we only need to explicitly pass an attr set if we need to override the default values that would have been present in the package set.

In nixpkgs, callPackage has been extended to include helpful package hints, and thus the complexity has grown, but the underlying intuition has remained the same.

In javascript, callPackage would be an example of a curried function, where there's an implicit package set bound to it.