Hacker symbol

September 21, 2022 ~ 3 min read

Nix function currying explainer


Table of Contents

Source

The source of the file is here: https://github.com/ne9z/dotfiles-flake

[Top]

Function Currying

Since nix is a functional language with some similarities to Haskell, functions can be declared with just a colon.

doubleIt = a: a * 2

The colon defines a function that has a as a paremeter.

One function can call another function and another in a nearly endless call.

nix-repl> myFunc = a: b: c: a + b + c
nix-repl> myFunc "x" "y" "z"
"xyz"

The benefit is that you can call only 2 variables and return a function that can later be used in another setting.

nix-repl> newFunc = myFunc "hello" "world"

nix-repl> newFunc "!"
"helloworld!"

Above, newFunc is a function that is defined as:

newFunc = a: "helloworld" + a

[Top]

Function Attributes

Imports

Functions

This is the code:

mkHost = hostName: systemFunc: (
    ({my-config, zfs-root, pkgs, system, ... }:
      nixpkgs.lib.nixosSystem {
        inherit system;
        modules = [
            ./modules
            (import ./hosts/${hostName}/configuration.nix { inherit pkgs; })
            (({ my-config, zfs-root, pkgs, lib, ... }: {
                inherit my-config zfs-root;
                system.stateVersion = "23.05";
            }) {
                inherit my-config zfs-root pkgs;
                lib = nixpkgs.lib;
            })
            (import ./configuration.nix { inherit pkgs; })
        ];
    })
    (import ./hosts/${hostName} {
        system = system;
        pkgs = nixpkgs.legacyPackages.${system};
    })
);

Note, the above has been simplified and it is likely not to work.

There is a lot of moving pieces above! Namely:

  • ./hosts/${hostName}
  • ./hosts/${hostName}/configuration.nix
  • ./modules

mkHost is a function that takes in two parameters, hostName, and systemFunc and each of those parameters represents another function. Remember that the : is enough to define a function.

The systemFunc function is defined as:

(import ./hosts/${hostName} {
    system = system;
    pkgs = nixpkgs.legacyPackages.${system};
})

The hostName function is defined as:

({my-config, zfs-root, pkgs, system, ... }:
  nixpkgs.lib.nixosSystem {
    inherit system;
    modules = [
        ./modules
        (import ./hosts/${hostName}/configuration.nix { inherit pkgs; })
        (({ my-config, zfs-root, pkgs, lib, ... }: {
            inherit my-config zfs-root;
            system.stateVersion = "23.05";
        }) {
            inherit my-config zfs-root pkgs;
            lib = nixpkgs.lib;
        })
        (import ./configuration.nix { inherit pkgs; })
    ];
})

Which in itself has multiple inline functions defined. hostName has another function in it that needs as its inputs the following attributes: my-config, zfs-root, pkgs, and system. These attributes are returned from the systemFunc function, because the systemFunc function call another function in it. Namely:

import ./hosts/${hostName}

The contents of this ./hosts/${hostName}/default.nix file are:

{
  system,
  pkgs,
}: {
  inherit pkgs system;
  zfs-root = {
    ...
  };
  my-config = {
    ...
  };
}

Which, you guessed it, is a function { system, pkgs}: that takes two parameters and returns and attribute set of:

{
    system = {...};
    pkgs = {...};
    zfs-root {...};
    my-config = {...};
}

Which satisfies the parameters of hostName!

Question for the reader. Why can't I add more attribute sets to ./hosts/${hostName}/default.nix ?

> Answer

Because hostName function expects only 4 named attributes as its inputs. You can add more attributes to the return of ./host/${hostName}/default.nix, it is just not being used anywhere in the attribute set input arguments.

[Top]


Sebastian BolaƱos

Hi, I'm Sebastian. I'm a software developer from Costa Rica. You can follow me on Twitter. I enjoy working on distributed systems.