Nix is Functional, Lazy evaluated.

interactive interpreter: nix repl. Execute a file containing nix expression: nix-instantiate --eval file.nix.

let expression (let binding): specify binding scope

  • e.g. let a=1; b=2; in a+b -> 3

datatypes:

  • attribute set { name1 = ...; name2 = ...; }, where ... can be a string, number, list or attribute set.

    • rec attribute set: rec { ... }, allow access to attributes within the set itself.

      • e.g. rec {a = 1; b = a + 1;} -> {a = 1; b = 2;}
    • attribute access with .

      • e.g. let attrset = {a.b.c = 1}; in attrset.a.b -> {c = 1;}
  • list [a b c], separated by white space

  • bool true, false

  • inline comment #...

  • string "something", multiline string uses double single quotes ''some\n\tthing''

with expression: access attributes without repeatedly referencing the attribute set

  • e.g. let a={x=1; y=2;}; in with a; [x y] -> [ 1 2 ]

inherit expression: shorthand for assigning value from existing scope to the same name in a nested scope

  • e.g. let x=1; y=2; in {inherit x y;} -> {x=1; y=2;}

  • e.g. (specify attrset) let a={x=1; y=2;} in {inherit (a) x y;} -> {x=1; y=2;}

  • e.g. (in let expr) let inherit ({x=1; y=2;}) x y; in [x y] -> [1 2]

string interpolation: ${...}

  • e.g. let name="Nix"; in "Hello ${name}" -> "Hello Nix

paths

  • absolute and relative paths are similar to bash

  • lookup paths (angle bracket paths)

    • depends on the value of builtins.nixPath

    • Not recommended due to impurities

    • e.g. <nixpkgs>/lib -> "/nix/var/nix/profiles/per-user/root/channels/nixpkgs/lib"

Functions

  • Whenever a colon (:) appears, it’s a function. Functions are always anonymous.

  • Single argument

x: x + 1
  • Multiple arguments via nesting
x: y: x + y
  • Attribute set argument
{ a, b }: a + b
  • With default attributes
{ a, b ? 0 }: a + b
  • With additional attributes allowed
{ a, b, ...}: a + b
  • Named attribute set argument
args@{ a, b, ... }: a + b + args.c

or

{ a, b, ... }@args: a + b + args.c

Calling Functions

  • use let bindings to bind a name for a function
let f = x : x + 1; in f 1

or

let f = x: x.a; in f { a = 1; }
  • use parentheses when do not need a name
(x: x + 1) 1

Function Libraries

  • builtins

    • most builtins are accessible through builtins
a = "Hell${builtins.toString 0} World!";

or

a = "Hell${toString 0} World!";
+ import

  + takes a path to a Nix file, reads it and evaluate the contained nix expression, and returns the result value.
  • pkgs.lib

    • The nixpkgs repository contains an attribute set called lib, which provides a large number of useful functions. They are implemented in the Nix language, as opposed to builtins, which are part of the language itself.
let
  pkgs = import <nixpkgs> {};
in
pkgs.lib.strings.toUpper "lookup paths considered harmful"

Fetchers

builtins.fetchurl "https://github.com/NixOS/nix/archive/7c3ab5751568a0bc63430b33a5169c5e4784a0f

Derivations

  • The Nix language primitive to declare a derivation is the built-in impure function derivation.

  • It is usually wrapped by the Nixpkgs build mechanism stdenv.mkDerivation, which hides much of the complexity involved in non-trivial build procedures.

  • Whenever you encounter mkDerivation, it denotes something that Nix will eventually build.

Worked Examples

  • Shell environment
{ pkgs ? import <nixpkgs> {} }:
let
  message = "hello world";
in
pkgs.mkShellNoCC {
  buildInputs = with pkgs; [ cowsay ];
  shellHook = ''
    cowsay ${message}
  '';
}
  • NixOS configuration
{ config, pkgs, ... }: {
  imports = [ ./hardware-configuration.nix ];
  environment.systemPackages = with pkgs; [ git ];
  # ...
}