The Nix Expression Language

Nix is a lazy, purely functional language which you'll be using to configure your system. Before we discuss configuration in detail, you should be familiar with its basic syntax.

Types

  • Strings:
    "Hello, world!"
    ''First Line
      Second Line
    ''
    
  • Integers and Floating-Point Numbers:
    1
    3.14
    
  • Filesystem Paths:
    /path/to/file
    ./hardware-configuration.nix
    ./. #! current directory: just `./` is not valid
    
    #! paths in angle brackets search the contents of $NIX_PATH. $NIX_PATH can
    #! have {key}={value} entries for special prefixes.
    
    #! `<nixpkgs>` is the path to your system's local copy of `nixpkgs`, located
    #! at `/nix/var/nix/profiles/per-user/root/channels/nixos`,
    <nixpkgs/pkgs/tools/misc/cowsay>
    
    #! and `<nixos-config>` is the path `/etc/nixos/configuration.nix`
    <nixos-config>
    
  • URLs (don't use these, they're deprecated!):
    https://github.com/NixOS/rfcs/blob/master/rfcs/0045-deprecate-url-syntax.md
    
  • Booleans:
    true
    false
    
  • Null:
    null;
    
  • Lists, whose values can be any type:
    [ 1 "two" [ 3.0 ] { four = 4; } ]
    
  • Sets (like dicts, maps), whose keys are strings and whose values can be any type:
    { a = 1; b = [ 2 ]; c = { x = 3; }; }
    
    #! keys can be quoted if they have special characters that can't be there
    #! normally
    { "blah..." = 1; }
    
    #! make sure you don't forget those semicolons!
    
  • Functions, which take exactly one argument and return the result of exactly one expression:
    #! these commands are performed in `nix repl` if you want to try them yourself.
    
    f = n: n + 1
    x = f 1 #: 2
    
    #! you can use currying to create functions which take multiple arguments
    f = first: second: first + second
    x = f 1 2 #: 3
    
    #! or you can destructure a set argument in the function declaration like so
    f = { first, second }: first + second
    x = f { first = 1; second = 2; } #: 3
    
    #! you can also accept a set with unspecified extra attributes (...), and
    #! specify defaults for optional attributes (attrib ? default)
    f = { first, second ? 2, ... }: first + second
    x = f { first = 1; irrelevant = 5; } #: 3
    
    #! and even access the whole set by name to get to those extra attributes
    #! (name@{ ... } or { ... }@name)
    f = input@{ first, second, ... }: first + second + input.third
    x = f { first = 1; second = 2; third = 3; } #: 6
    

Operators

set.x # attribute access
f x   # function application

-x # numerical negation

# attribute existence testing (true if `set` contains an attribute `x`)
set ? x

list ++ list # list concatenation

x * y / z # multiplication and division
x + y - z # addition and subtraction

!a # logical negation

# set merging (a set with both sets' attributes, if both sets have an attribute
# with the same key then `set2` takes precedence)
set1 // set2

a<b<=c>=d>e  # comparison
a == b != c  # equality testing
a && b       # logical conjunction
a || b       # logical disjunction

# logical implication (if `a` is true, `b` needs to be true or else the result
# is false)
a -> b

Builtins

abort s # no return value. stop evaluation and print error string

baseNameOf s # part of `s` after its last '/' character

derivation inputs # a derivation. will be discussed later

dirOf s # part of `s` preceding `baseNameOf s`

fetchTarball url # path of unpacked .tar.(gz|xz|bz2) downloaded from `url`

import path # the expression at `path`

# whether `x` is null. this is deprecated because you can just write `x == null`
isNull x

map f list # list of each `f x` for each `x` in `list`

removeAttrs set list # `set` with each attribute in `list` removed

throw s # no return value. `abort s` but softer

toString x # `x` converted to a string

builtins # a set containing more builtins

The remaining built-in functions are kept in builtins to prevent clutter. The contents of that set are described in Nix Manual: 15.5. Built-in Functions.

Other Features

# comments, as you've probably noticed, begin like this
/* multi-line comments, however,
   use these */

# bring a set's attributes into scope
s = { a = 1; };
x = with s; [ a ]; #: [ 1 ]

# define some temporary local variables to use in an expression
x = let
  a = 1;
  b = 2;
in a + b; #: 3

# allow a set to reference its own attributes
x = rec {
  a = 1;
  b = a + 1;
}; #: { a = 1; b = 2; }

# inherit some variables from the surrounding scope
x = rec {
  a = 1;
  b = {
    inherit a;
  }
}; #: { a = 1; b = { a = 1; }; }

# or from another set
x = rec {
  a = { b = 1; };
  inherit (a) b;
}; #: { a = { b = 1; }; b = 1; }

see also Nix Manual: Part IV. Writing Nix Expressions or NixOS Wiki: Nix Expression Language