Overview of the Nix Language: Difference between revisions
imported>Makefu add sets, functions, import |
imported>Makefu good enough i guess |
||
Line 1: | Line 1: | ||
This [[:Category:Discussion|discussion]] article covers the syntax, semantics, typing, compilation, tooling and libraries of the Nix Expression Language. | This [[:Category:Discussion|discussion]] article covers the syntax, semantics, typing, compilation, tooling and libraries of the Nix Expression Language. |
Revision as of 23:58, 9 December 2017
This discussion article covers the syntax, semantics, typing, compilation, tooling and libraries of the Nix Expression Language.
The Nix expression language is a pure, lazy, functional language. Purity means that operations in the language don't have side-effects (for instance, there is no variable assignment). Laziness means that arguments to functions are evaluated only when they are needed. Functional means that functions are “normal” values that can be passed around and manipulated in interesting ways. The language is not a full-featured, general purpose language. Its main job is to describe packages, compositions of packages, and the variability within packages.
— From the nix manual
The language was designed especially for the Nix Package Manager.
Language Paradigms
Lazy
Not all expressions in nixpkgs will be evaluated and instantiated as nix performs evaluation only when needed for a finished output. In the following example abort
will never be triggered as the variable it belogs to is unused:
let
a = abort "will never happen";
b = "hello";
c = "world"
in b + c
Functional
Functional Programming is a style of building the structure and elements of computer programs—that treats computation as the evaluation of mathematical functions and avoids changing-state and mutable data. It is a declarative programming paradigm, which means programming is done with expressions or declarations instead of statements.
see also: [1]
Pure
A pure function is a function where the return value is only determined by its input values, without observable side effects. In Nix, all build operations try to be as pure as possible to achieve reproducible builds. This means that wherever you build the packages as few side effects as possible should have an impact onto the build.
Language Features
This section describes the main language features of the nix expression language
expressions
When Nix tutorials talk about Nix Expressions they typically mean the definition of a function with multiple inputs which as a result in a derivation. However a Nix expression can be everything, from a simple string, to function to a set of expressions.
types
The nix language provides a number of basic types:
Type | Description | Example |
---|---|---|
Strings | Strings either start with double quotes or double single quotes . They also support antiquotation (templating). Leading spaces are stripped with double single quotes. |
"Say ${pkgs.hello.name}" ,
''first line
second line
''
|
Integers | There is no float in the nix language | 5
|
Path | relative paths will become to absolute when evaluated,paths must contain a slash. <nixpkgs/pkgs> is also possible and will resolve to the folder incl. subfolders in your NIX_PATH |
./hello/world
> /abs/path/to/hello/world <nixpkgs/lib>
> /path/to/your/nixpkgs/lib |
URI | http://example.org/foo.tar.bz2
| |
Boolean | true ,false
| |
Null | null
| |
Lists | items are not separated by comma, can contain any other type | [ 1 ./example.bin { hello="world"; }]
|
Sets | In other languages called dicts (py),objects (js) hashes (ruby), essentially a list of key-value pairs |
{ hello="world"; }.hello
> "world"
|
Functions | see below | pattern: body
|
A detailed description of all types can be found in The Nix manual
functions
Functions have the following form: pattern: body
There are 3 patterns in how functions can be written:
f = a: a*a;
a single identifier, can be extended with multiple identifiers asf = a: b: a*b;
as a partial function would be returned.{ x, y ? "foo", z ? "bar", ... }: z + y + x
a set pattern, which can also set defaults. ellipse means that the function may receive extra arguments.args@{ x, y, z, ... }: z + y + x + args.a
an@
pattern which can be used to match extra arguments and store them in theargs
set.
imports
import
loads, parses and imports the nix expression stored in path. This keyword is essentially a builtin of nix but not a part of the language itself.
Usage:
x = import <nixpkgs> {};
y = trace x.pkgs.hello.name x;
notable constructs
Nix looks a lot like JSON with functions but also provides a number of very specialized constructs which can help you build clean and easy to read expressions. In this sub-chapter the most notable constructs will be shown by example:
with
statement
The with
statement introduces the lexical scope of a set into the expression which follows.
Common usages are:
On top of expressions:
You will see the with statement a lot at the beginning of expression definition. Most of the time it is used to load the lib functions into the namespace for quick access.
{lib, ... }:
with lib;
{
options = {
networking.hosts = mkOption {
type = with types; attrsOf ( listOf str);
default = {};
};
};
...
}
instead of:
{lib, ... }:
{
options = {
networking.hosts = lib.mkOption {
type = lib.types.attrsOf ( lib.types.listOf lib.types.str);
default = {};
};
};
...
}
In package input definitions:
{pkgs}:
{
...
buildInputs = with pkgs; [ curl php coreutils procps ffmpeg ];
}
Instead of :
{pkgs}:
{
...
buildInputs = [ pkgs.curl pkgs.php pkgs.coreutils pkgs.procps pkgs.ffmpeg ];
}
In the package meta tag:
{stdenv, ...}:
{
...
meta = with stdenv.lib; {
license = with licenses; [ lgp3 gpl3 ];
maintainers = with maintainers; [ adisbladis lassulus ];
};
}
Instead of :
{stdenv, ...}:
{
...
meta = {
license = [ stdenv.lib.licenses.lgp3 stdenv.lib.licenses.gpl3 ];
maintainers = [ stdenv.lib.maintainers.adisbladis stdenv.lib.maintainers.lassulus ];
};
}
In a default.nix of an external package:
with import <nixpkgs> {};
stdenv.mkDerivation rec {
name = "mytool-env";
src = ./.;
buildInputs = with pkgs;[
python34
python34Packages.docopt
];
shellHook =''
export HISTFILE=$PWD/histfile
'' ;
}
let ... in
statement
With let
you can define local variables which can also reference to self without the need of the rec
construct. This feature is use inside expressions to prepare variables which become part of an output.
The usage of let
is comparable to the Haskell let expression
let
a = 1;
b = 2;
in a + b
=> 3
inherit
statement
The inherit
expression can be used to copy variables from the surrounding lexical scope. A typical use case is to declare the version or name of a derivation in the expression and reuse this parameter in the function to fetch the source.
This is a typical python package derivation as the fetchPypi function also requires pname
and version
as input:
buildPythonPackage rec {
pname = "hello";
version = "1.0";
src = fetchPypi {
inherit pname version;
sha256 = "01ba..0";
};
}
rec
statement
The rec
expression turns a basic set into a set where self-referencing is possible. This can be used when the let
expression would create too much clutter. It is often seen in package derivation descriptions.
Sample usage:
rec {
x = y - 100;
y = 123;
}.x
=> 23
Learning Resources
The manual provides a reference of the Nix language. All language constructs you may use in nix are defined here, together with code snippets.
Nix By Example is a step-by-step tutorial. The nix pills also provide a lot of insight into the language and functional package management in general.
Development Tools
Syntax Highlighting & Editor Modes
Nix language has decent syntax highlighting (SH) support among popular code editors, but refactoring/autocomplete is still rare.
Reference: Editor Modes for Nix Files