status
this chapter is in active development
expect live edits and rapid iteration (except for when i am really busy with other stuff) while this material is written.
functions
single-argument functions, currying, patterns, and builtins.
status
this chapter is in active development
expect live edits and rapid iteration (except for when i am really busy with other stuff) while this material is written.
single-argument functions, currying, patterns, and builtins.
every function takes exactly one argument.
f = x: x + 1
f 3 # 4
x: x + 1. part before the colon is the argument. part after is the body. no parentheses, no =>, no fun keyword.
two arguments? a function that returns a function:
add = a: b: a + b
add 10 3 # 13
a: b: a + b is a: (b: a + b). calling add 10 does not error. it returns a new function:
addTen = add 10
addTen 3 # 13
addTen 7 # 17
add = a: b: a + badd 10=b: 10 + badd 10 3=13nixpkgs uses partial application everywhere. callPackage works because of it.
function application is a space. f x calls f with x. multiple arguments chain: f a b c is ((f a) b) c.
no parentheses unless the argument is a compound expression:
f = x: x * 2
f 3 + 1 # 7
f (3 + 1) # 8
f 3 + 1 is (f 3) + 1. function application binds tighter than arithmetic.
in practice, most nix functions destructure an attribute set:
{ pname, version, src }:
stdenv.mkDerivation {
inherit pname version src;
}
{ pname, version, src }: is a pattern. nix matches the argument and binds each name.
{ pname, version, src, debug ? false }:
caller omits debug, it defaults to false.
greet = { name, greeting ? "hey" }: "${greeting}, ${name}""hello, nix""hey, nix"... for extra attributespassing an attribute not in the pattern is an error:
f = { a, b }: a + b
f { a = 1; b = 2; c = 3; }
# error: function called with unexpected argument 'c'
... accepts and ignores extras:
f = { a, b, ... }: a + b
f { a = 1; b = 2; c = 3; } # 3
@sometimes you need both the destructured names and the whole set:
{ pname, version, ... } @ args:
stdenv.mkDerivation (args // {
meta.description = "${pname} ${version}";
})
@ args binds the entire argument to args. destructure what you need, pass the rest through.
built-in functions living in the builtins set:
builtins.length [ 1 2 3 ] # 3
builtins.map (x: x * 2) [ 1 2 3 ] # [ 2 4 6 ]
builtins.filter (x: x > 2) [ 1 2 3 4 5 ] # [ 3 4 5 ]
builtins.attrNames { b = 2; a = 1; } # [ "a" "b" ]
| builtin | what it does |
|---|---|
map f list | apply f to each element |
filter f list | keep elements where f returns true |
attrNames set | keys, sorted |
attrValues set | values, in key-sorted order |
hasAttr name set | whether the key exists |
elem x list | whether x is in the list |
foldl' f init list | strict left fold |
import path | evaluate a nix file, return its value |
throw msg | abort evaluation |
import ./foo.nix evaluates the file and returns whatever it produces. function? you get a function. attribute set? you get a set. no module system at this level. files evaluate to values.
{ lib, stdenv, fetchurl, openssl ? null }:
stdenv.mkDerivation { ... }
single-argument function. argument is an attribute set. lib, stdenv, fetchurl destructured. openssl defaults to null. callPackage in nixpkgs calls this function with the right arguments. packages never import their dependencies manually.
lazy evaluation is the last language concept, and the one that makes the whole ecosystem possible.