primitives
strings, numbers, paths, booleans, lists, and null.
strings, numbers, paths, booleans, lists, and null.
nine types. seven primitives, plus attribute sets and functions (which get their own pages).
"hello"double-quoted or multi-line with ''4264-bit signed. overflow is a hard error3.1464-bit. rare in practicetruetrue or false. lowercasenulla distinct type, separate from false and 0./srcresolves relative to the file, not the shell[ 1 2 3 ]ordered, heterogeneous. spaces, no commas{ a = 1; }next pagex: x + 1two pages outfire up nix repl and follow along.
"hello" + " " + "world"
# "hello world"
interpolation with ${}:
let name = "curl"; in "package: ${name}"
# "package: curl"
the expression inside ${} must evaluate to a string. nix does not silently coerce integers. "count: ${42}" is an error. use toString:
let n = 42; in "count: ${toString n}"
# "count: 42"
two single quotes ('') delimit multi-line strings. nix strips common leading whitespace:
''
first line
second line
''
# "first line\nsecond line\n"
this matters when you embed shell scripts in derivations:
buildPhase = ''
mkdir -p $out/bin
gcc -o $out/bin/hello hello.c
'';
the two spaces before mkdir are relative to the delimiters. they get stripped.
1 + 2 # 3
7 / 2 # 3
integer division truncates.
floats exist (1.0 + 2.5 gives 3.5) but are rare. you will almost never write one.
true && false # false
!true # false
if true then "yes" else "no"
# "yes"
if is an expression. it returns a value. the else branch is required.
null == null # true
null == false # false
its own type. no implicit conversions.
paths are not strings.
./src
# /home/karol/project/src
./.
# /home/karol/project
relative paths resolve against the directory of the nix file that contains them. not the working directory of your shell. ./src in a default.nix always points to the src next to that file, regardless of where you run nix-build.
interpolate a path into a string and nix copies it into the store:
"${./hello.c}"
# "/nix/store/...-hello.c"
that is how source files enter the build system.
import <nixpkgs>
<nixpkgs> is a lookup against NIX_PATH. it resolves to wherever your system has nixpkgs. flakes replace this, but you will see it in older code.
ordered, heterogeneous, space-separated. no commas.
[ 1 2 3 ]
[ "hello" 42 true null ]
[ 1 2 ] ++ [ 3 4 ]
# [ 1 2 3 4 ]
++ concatenates. no index operator. builtins.elemAt does it:
builtins.elemAt [ "a" "b" "c" ] 1
# "b"
lists show up in buildInputs, propagatedBuildInputs, meta.platforms. you rarely need to index into them.
builtins.typeOf "hello" # "string"
builtins.typeOf 42 # "int"
builtins.typeOf ./src # "path"
builtins.typeOf { a = 1; } # "set"
builtins.typeOf (x: x) # "lambda"
attribute sets are "set". functions are "lambda". these show up in error messages.
attribute sets is the data structure everything in nix revolves around: nesting, merging, recursive sets, let...in, with, and inherit.