Nix language
imagine the Nix language as JSON with functions.
Nix language data types without functions work just like their counterparts in JSON and look very similar.
Nix Language Overview
-
Domain-specific, purely functional, lazily evaluated, and dynamically typed.
-
Designed for creating and composing derivations (precise descriptions of how existing files are used to generate new ones).
-
Notable Uses of Nix Language:
- Nixpkgs:
- Largest, most up-to-date software distribution written in Nix.
- NixOS:
- Linux distribution configured declaratively.
- Based on Nix and Nixpkgs.
- Modular configuration system and environment are written in Nix.
- Uses packages from Nixpkgs for OS configuration and services.
- Nixpkgs:
-
Complexity in Nix Expressions:
- Nix expressions can seem complicated.
- Complexity reflects the problem’s nature and how well it is understood.
- Building software is inherently complex; Nix helps manage this complexity.
-
Core Concepts:
- Despite initial complexity, Nix has only a few basic concepts that can be combined in various ways.
- The complexity often stems from how the language is applied, rather than from the language itself.
Using in Practice
-
Language:
- Syntax and semantics of the Nix programming language.
-
Libraries:
- Includes built-ins and
pkgs.lib.
- Includes built-ins and
-
Developer Tools:
- Tools for testing, debugging, linting, formatting, etc.
-
Generic Build Mechanisms:
- Includes
stdenv.mkDerivation, trivial builders, and more.
- Includes
-
Composition and Configuration Mechanisms:
- Tools like
override,overrideAttrs,overlays,callPackage, etc.
- Tools like
-
Ecosystem-Specific Packaging Mechanisms:
- Packaging methods such as
buildGoModule,buildPythonApplication, etc.
- Packaging methods such as
-
NixOS Module System:
- Consists of
config,option, and other components.
- Consists of
-
Key Concepts:
- A piece of Nix language code is referred to as a Nix expression.
- Evaluating a Nix expression produces a Nix value.
- The content of a Nix file (with a
.nixextension) is a Nix expression.
Interactive Evaluation with nix repl
-
Use the Nix REPL to evaluate Nix expressions interactively from the command line:
nix repl-
Example session:
nix-repl> 1 + 2 3
-
-
Lazy Evaluation in Nix:
- By default, Nix uses lazy evaluation, meaning values are computed only when needed.
- To see a fully evaluated data structure, use
:pbefore the expression.
-
Examples:
-
Without
:p(default lazy evaluation):nix-repl> { a.b.c = 1; } { a = { ... }; } -
With
:p(fully evaluated):nix-repl> :p { a.b.c = 1; } { a = { b = { c = 1; }; }; }
-
-
Exiting the REPL:
- Type
:qto exit the Nix REPL.
- Type
Evaluating Nix Files
-
Use
nix-instantiate --evalto evaluate the expression in a Nix file:echo 1 + 2 > file.nix nix-instantiate --eval file.nix-
Output:
3
-
-
Default File Evaluation:
-
If no file name is specified,
nix-instantiate --evalwill try to read fromdefault.nix:echo 1 + 2 > default.nix nix-instantiate --eval-
Output:
3
-
-
-
Lazy Evaluation:
- Nix uses lazy evaluation, meaning values are only computed when needed.
- To ensure full evaluation of data structures, use the
--strictoption.
-
Example:
-
Without
--strict(lazy evaluation):echo "{ a.b.c = 1; }" > file.nix nix-instantiate --eval file.nix-
Output:
{ a = <CODE>; }
-
-
With
--strict(full evaluation):echo "{ a.b.c = 1; }" > file.nix nix-instantiate --eval --strict file.nix-
Output:
{ a = { b = { c = 1; }; }; }
-
-
Values in Nix
-
Primitive data types, lists, attribute sets, and functions.
-
Attribute sets and let expressions are used to assign names to values.
-
Assignments use a single equal sign (
=), with the name on the left and the value on the right, ending with a semicolon (;). -
Attribute Sets:
-
A collection of unique name-value pairs.
-
Example showing all data types, lists, and attribute sets:
{ aNumber = 42; aString = "hello"; aList = [ 1 2 3 ]; anAttrSet = { key = "value"; }; } -
Attribute names typically do not require quotes.
-
List elements are separated by white space.
-
-
Recursive Attribute Sets (
rec { ... }):-
Declaring an attribute set with
recallows accessing attributes from within the set. -
Example:
rec { one = 1; two = one + 1; three = two + 1; }- Output:
{ one = 1; two = 2; three = 3; }
- Output:
-
-
Order in Attribute Sets:
-
The order of elements in an attribute set does not matter for evaluation.
-
Without
rec, attempting to reference an undefined variable will result in an error. -
Example error:
{ one = 1; two = one + 1; three = two + 1; }- Results in:
error: undefined variable 'one'
- Results in:
-
Let Expressions (let ... in ...)
-
Also called let binding, this allows assigning names to values for repeated use within a specific scope.
-
Basic Structure:
-
letintroduces the assignments, followed byin, where the expression using those assignments is defined. -
Example:
let a = 1; in a + a- Output:
2
- Output:
-
-
Order of Assignments:
-
Names can be assigned in any order, and expressions can refer to other assigned names.
-
Example:
let b = a + 1; a = 1; in a + b- Output:
3
- Output:
-
-
Local Scope of Let Bindings:
-
Names declared within the
letexpression are only accessible within that expression (local scope). -
Counter-example:
- Attempting to access a name outside its scope results in an error:
{ a = let x = 1; in x; b = x; }-
Error:
error: undefined variable 'x'
-
Attribute Access
-
Attributes in a set are accessed using the dot (
.) notation followed by the attribute name. -
Example:
let attrset = { x = 1; }; in attrset.x- Output:
1
- Output:
-
Accessing Nested Attributes:
-
The same dot notation is used for accessing nested attributes.
-
Example:
let attrset = { a = { b = { c = 1; }; }; }; in attrset.a.b.c- Output:
1
- Output:
-
-
Assigning Attributes with Dot Notation:
-
The dot notation can also be used to assign attributes.
-
Example:
{ a.b.c = 1; }- Output:
{ a = { b = { c = 1; }; }; }
- Output:
-
with ...; ... Expression
-
The
withexpression provides access to attributes without needing to repeatedly reference their attribute set. -
Basic Usage:
-
Example:
let a = { x = 1; y = 2; z = 3; }; in with a; [ x y z ]- Output:
[ 1 2 3 ]
- Output:
-
The above is equivalent to:
[ a.x a.y a.z ]
-
-
Scope of
with:- Attributes made available via
withare only in scope for the expression following the semicolon (;).
- Attributes made available via
-
Counter-example:
-
Attempting to access an attribute outside the
withscope results in an error:let a = { x = 1; y = 2; z = 3; }; in { b = with a; [ x y z ]; c = x; # Error: x is out of scope here }-
Error:
error: undefined variable 'x'
-
-
inherit Expression
-
inheritis shorthand for assigning the value of a name from an existing scope to the same name in a nested scope, avoiding repetition. -
Basic Usage:
-
Example:
let x = 1; y = 2; in { inherit x y; }- Output:
{ x = 1; y = 2; }
- Output:
-
This is equivalent to:
{ x = x; y = y; }
-
-
inheritfrom a Specific Attribute Set:-
You can inherit values from a specific attribute set using parentheses.
-
Example:
let a = { x = 1; y = 2; }; in { inherit (a) x y; }- Output:
{ x = 1; y = 2; }
- Output:
-
This is equivalent to:
{ x = a.x; y = a.y; }
-
-
inheritinletExpressions:-
inheritcan also be used insideletexpressions. -
Example:
let inherit ({ x = 1; y = 2; }) x y; in [ x y ]- Output:
[ 1 2 ]
- Output:
-
This is equivalent to:
let x = { x = 1; y = 2; }.x; y = { x = 1; y = 2; }.y; in [ x y ]
-
String Interpolation (${ ... })
-
Nix allows inserting the value of a Nix expression into a string using
${}. -
Example:
let name = "Nix"; in "hello ${name}"- Output:
"hello Nix"
- Output:
-
Restrictions:
-
Only character strings or values that can be converted to strings can be used in interpolation.
-
Counter-example:
- Attempting to use non-string types like integers without explicit conversion results in an error:
let x = 1; in "${x} + ${x} = ${x + x}"-
Error:
error: cannot coerce an integer to a string
-
-
Nesting Interpolations:
-
Nix supports nested interpolations, but this can make the code hard to read.
-
Example:
let a = "no"; in "${a + " ${a + " ${a}"}"}"- Output:
"no no no"
- Output:
-
-
Shell Script Variables vs. Interpolated Strings:
-
Strings using a dollar sign without braces (e.g.,
$out) are not interpolated strings in Nix. -
This notation typically refers to shell script variables, and any overlap with Nix variables is coincidental.
-
Example:
let out = "Nix"; in "echo ${out} > $out"- Output:
"echo Nix > $out"
- Output:
-
File System Paths in Nix
-
Absolute paths always start with a slash (
/).-
Example:
/absolute/path- Output:
/absolute/path
- Output:
-
-
Relative paths contain at least one slash (
/) but do not start with one. They are relative to the file containing the expression or the current directory in the REPL.-
Example:
./relative- Output:
/current/directory/relative
- Output:
-
Example:
relative/path- Output:
/current/directory/relative/path
- Output:
-
-
Dot Notation for Current Directory:
-
A single dot (
.) represents the current directory in a path. -
Example:
./.- Output:
/current/directory
- Output:
-
The combination
./.is a relative path that denotes the current directory.
-
-
Two Dots (
..) for Parent Directory:- Two dots (
..) represent the parent directory.-
Example:
../.- Output:
/current
- Output:
-
- Two dots (
-
Paths in Interpolated Expressions:
- Paths can be used inside interpolated strings, though this is considered an impure operation.
Lookup Paths (Angle Bracket Syntax)
-
Lookup paths use angle brackets (
<...>) and resolve to file system paths. -
The value of a lookup path depends on the value of
builtins.nixPath. -
Example:
-
Lookup path:
<nixpkgs>- Output:
/nix/var/nix/profiles/per-user/root/channels/nixpkgs
- Output:
-
Accessing a subdirectory:
<nixpkgs/lib>- Output:
/nix/var/nix/profiles/per-user/root/channels/nixpkgs/lib
- Output:
-
-
Practical Usage:
<nixpkgs>typically points to the file system path of a particular Nixpkgs revision.- Lookup paths are often seen in examples, but their use in production code is discouraged due to their impure, non-reproducible nature.
Indented Strings (Multi-line Strings)
-
Denoted by double single quotes (
'' ''), used for character strings that span multiple lines with common indentation. -
Basic Example:
'' multi line string ''- Output:
"multi\nline\nstring\n"
- Output:
-
White Space Trimming:
-
Equal amounts of prepended white space are trimmed automatically from the result.
-
Example:
'' one two three ''- Output:
"one\n two\n three\n"
- Output:
-
Functions in Nix
-
Functions in Nix always take exactly one argument.
-
The argument is on the left of the colon (
:) and the function body is on the right. -
Function Declarations:
- Functions are anonymous (also called lambdas).
- Functions can be assigned to a name, just like other values.
-
Basic Function Forms:
-
Single Argument:
-
Example:
x: x + 1 -
This creates a function that adds 1 to
x.
-
-
Multiple Arguments (via nesting):
-
Example:
x: y: x + y -
This is a function that takes two arguments (
xandy) and adds them together.
-
-
Attribute Set Argument:
-
Example:
{ a, b }: a + b -
This function takes an attribute set with
aandb, and adds their values.
-
-
Attribute Set with Default Values:
-
Example:
{ a, b ? 0 }: a + b -
This function sets
bto 0 if not provided.
-
-
Attribute Set with Additional Attributes Allowed:
-
Example:
{ a, b, ... }: a + b -
This allows additional attributes besides
aandb.
-
-
Named Attribute Set Argument:
-
Example:
args@{ a, b, ... }: a + b + args.c -
This function takes an attribute set and allows access to the full attribute set via
args(e.g.,args.c).
-
-
-
Assigning Functions to Names:
-
Functions can be stored in variables for reuse.
-
Example:
let f = x: x + 1; in f- Output:
<LAMBDA>(indicating the value is a function).
- Output:
-
Calling Functions (Function Application)
-
To call a function with an argument, write the argument after the function.
-
Basic Function Call:
-
Example:
let f = x: x + 1; in f 1- Output:
2
- Output:
-
-
Calling a Function on an Attribute Set:
-
Example:
let f = x: x.a; in f { a = 1; }- Output:
1
- Output:
-
-
Passing Arguments by Name:
-
You can also pass named arguments:
let f = x: x.a; v = { a = 1; }; in f v- Output:
1
- Output:
-
-
Using Parentheses for Clarity:
-
Parentheses are sometimes required to distinguish the function from the argument:
(x: x + 1) 1- Output:
2
- Output:
-
-
Function Application vs. List Construction:
-
Example 1: Applying
ftoaand placing the result in a list:let f = x: x + 1; a = 1; in [ (f a) ]- Output:
[ 2 ](a list with one element: the result off a).
- Output:
-
Example 2: Placing
fandaas separate elements in a list:let f = x: x + 1; a = 1; in [ f a ]- Output:
[ <LAMBDA> 1 ](a list with two elements: the functionfand the argumenta).
- Output:
-
Attribute Set Argument (Keyword Arguments / Destructuring)
-
Nix functions can require an attribute set as an argument, specifying the structure (expected attributes) within braces
{ }. -
This allows functions to destructure the attribute set into specific named values.
-
Basic Example:
{a, b}: a + b-
The function expects an attribute set with exactly two attributes:
aandb. -
Example usage:
let f = {a, b}: a + b; in f { a = 1; b = 2; }- Output:
3
- Output:
-
-
Error with Extra or Missing Attributes:
-
If the attribute set contains additional attributes, it will cause an error.
-
Counter-example:
let f = {a, b}: a + b; in f { a = 1; b = 2; c = 3; }-
Error:
error: 'f' called with unexpected argument 'c'
-
-
Default Values (Default Arguments)
-
In Nix, destructured arguments can have default values. This is done by placing a question mark (
?) between the attribute name and its default value. -
Attributes with default values are optional and do not need to be supplied when calling the function.
-
Example with Default Value:
let f = {a, b ? 0}: a + b; in f { a = 1; }- Output:
1(sincebdefaults to0).
- Output:
-
Example with Multiple Default Values:
let f = {a ? 0, b ? 0}: a + b; in f { } # Empty attribute set- Output:
0(bothaandbdefault to0).
- Output:
-
Allowing Additional Attributes:
-
Using the ellipsis (
...), you can allow the function to accept additional attributes that aren’t used in the function body. -
Example:
let f = {a, b, ...}: a + b; in f { a = 1; b = 2; c = 3; }- Output:
3(the extra attributecis ignored, but doesn’t cause an error).
- Output:
-
Named Attribute Set Argument (@ pattern or @ syntax)
-
An attribute set can be given a name to allow access to the whole set as well as individual attributes.
-
The at sign (
@) is used to bind the full attribute set to a name, either before or after the destructured attributes. -
Basic Syntax:
-
Example:
{a, b, ...}@args: a + b + args.c- Or equivalently:
args@{a, b, ...}: a + b + args.c- Both forms bind the full attribute set to
args, allowing access to extra attributes.
-
-
Example Usage:
let f = {a, b, ...}@args: a + b + args.c; in f { a = 1; b = 2; c = 3; }- Output:
6(sincea = 1,b = 2, andargs.c = 3).
- Output:
Function Libraries in Nix
- Besides built-in operators like
+,==, and&&, there are two widely used function libraries:builtins(primitive operations or primops)import
builtins
-
Built-in functions are part of the Nix interpreter, implemented in C++.
-
These functions are accessible via the
builtinsconstant. -
Example:
builtins.toString- Output:
<PRIMOP>(indicates a built-in function).
- Output:
import
-
importis a top-level function (not underbuiltins). -
It reads a path to a Nix file, evaluates the contained expression, and returns the result.
-
If the path is a directory,
importlooks fordefault.nixin that directory. -
Example:
echo 1 + 2 > file.niximport ./file.nix- Output:
3(the expression1 + 2is evaluated fromfile.nix).
- Output:
-
Using
importfor Functions:-
If a Nix file contains a function, it can be imported and immediately applied to arguments.
-
Example:
echo "x: x + 1" > file.niximport ./file.nix 1- Output:
2(the functionx: x + 1is applied to1).
- Output:
-
-
Note:
- Parentheses are necessary when separating function declaration from function application.
pkgs.lib in Nixpkgs
-
The Nixpkgs repository contains a widely used attribute set called
lib, which offers many useful functions implemented in the Nix language (unlikebuiltins, which are part of the language itself). -
These functions are typically accessed via
pkgs.lib. -
Import Nixpkgs and use a function from
pkgs.lib:let pkgs = import <nixpkgs> {}; in pkgs.lib.strings.toUpper "lookup paths considered harmful"- Output:
LOOKUP PATHS CONSIDERED HARMFUL
- Output:
Importing from a Pinned Nixpkgs Version
-
For reproducibility, it is good practice to pin the Nixpkgs version:
let nixpkgs = fetchTarball "https://github.com/NixOS/nixpkgs/archive/06278c77b5d162e62df170fec307e83f1812d94b.tar.gz"; pkgs = import nixpkgs {}; in pkgs.lib.strings.toUpper "always pin your sources"- Output:
ALWAYS PIN YOUR SOURCES
- Output:
Using pkgs as a Function Argument
-
You may see
pkgspassed as a function argument:{ pkgs, ... }: pkgs.lib.strings.removePrefix "no " "no true scotsman"-
You can evaluate this in a file using
nix-instantiate:nix-instantiate --eval file.nix --arg pkgs 'import <nixpkgs> {}'- Output:
"true scotsman"
- Output:
-
Direct Use of lib as an Argument
-
Sometimes,
libis passed directly instead ofpkgs:{ lib, ... }: let to-be = true; in lib.trivial.or to-be (! to-be)-
You can evaluate this with:
nix-instantiate --eval file.nix --arg lib '(import <nixpkgs> {}).lib'- Output:
true
- Output:
-
When Both pkgs and lib Are Passed
-
In some cases, both
pkgsandlibare passed as arguments to avoid repeated use ofpkgs.lib:{ pkgs, lib, ... }: # Use both pkgs and lib
Historical Functions in pkgs.lib
-
Some functions in
pkgs.libare equivalent tobuiltinswith the same name due to historical reasons. -
Impurities in Nix:
- Nix primarily deals with pure expressions (declaring data and transforming it with functions), but in practice, derivations need to interact with the outside world (e.g., files).
- Reading files from the file system as build inputs is the only relevant impurity in Nix.
Build Inputs
-
Build inputs are files or directories that derivations refer to when deriving new files.
-
In Nix, build inputs must be explicitly declared using:
- File system paths
- Dedicated functions
-
Nix Store:
-
Nix refers to files by their content hash.
-
Files and directories are copied to the Nix store during evaluation.
-
The Nix store path is in the format:
/nix/store/<hash>-<name> -
The file is copied to
/nix/storewhen it is referenced.
-
Paths in String Interpolation
-
When a file system path is used in string interpolation, the contents of the file are copied to the Nix store.
-
Example:
echo 123 > data"${./data}"- Output:
"/nix/store/h1qj5h5n05b5dl5q4nldrqq8mdg7dhqk-data"
- Output:
-
Nix evaluates the path to the corresponding Nix store path:
/nix/store/<hash>-<name>- The file is copied to the Nix store as a side effect, and an error occurs if the path does not exist.
-
For directories, the entire directory (including nested contents) is copied to the Nix store.
Fetchers
-
Nix has impure functions to fetch files over the network during evaluation, storing them in the Nix store:
builtins.fetchurlbuiltins.fetchTarballbuiltins.fetchGitbuiltins.fetchClosure
-
Example of fetching a file:
builtins.fetchurl "https://github.com/NixOS/nix/archive/7c3ab5751568a0bc63430b33a5169c5e4784a0ff.tar.gz"- Output:
"/nix/store/7dhgs330clj36384akg86140fqkgh8zf-7c3ab5751568a0bc63430b33a5169c5e4784a0ff.tar.gz"
- Output:
-
Example of fetching and unpacking a tarball:
builtins.fetchTarball "https://github.com/NixOS/nix/archive/7c3ab5751568a0bc63430b33a5169c5e4784a0ff.tar.gz"- Output:
"/nix/store/d59llm96vgis5fy231x6m7nrijs0ww36-source"
- Output:
-
If the network request fails, Nix throws an error.
-
Nixpkgs provides additional library functions for fetching files over the network.
Derivations in Nix
-
Derivations are fundamental to Nix and the Nix language:
- The Nix language describes derivations.
- Nix runs derivations to produce build results.
- Build results can be used as inputs for other derivations.
-
The primitive function to declare a derivation is the built-in
derivationfunction.- However, this is typically wrapped by
stdenv.mkDerivationin Nixpkgs to simplify complex build procedures. mkDerivationdenotes something Nix will eventually build.
- However, this is typically wrapped by
Example of a Package Using mkDerivation:
-
The result of
derivation(andmkDerivation) is an attribute set with a special property: it can be used in string interpolation to evaluate to the Nix store path of its build result. -
Example:
let pkgs = import <nixpkgs> {}; in "${pkgs.nix}"-
Output (example):
"/nix/store/sv2srrjddrp2isghmrla8s6lazbzmikd-nix-2.11.0"
-
How Derivation Outputs Work
-
A derivation’s output path is fully determined by its inputs. In this case, inputs come from a particular version of Nixpkgs.
-
String interpolation allows referring to derivation results by their Nix store paths.
-
Why Use
mkDerivation:mkDerivationsimplifies build processes by hiding complex details.- The resulting string (from interpolating a derivation) is the file system path where the build result will end up in the Nix store.
Usage in Complex Derivations
- String interpolation on derivations enables referring to build results when declaring new derivations.
- This allows for constructing complex compositions of derivations in the Nix language by chaining their build results as inputs for other derivations.
Avoiding Lookup Paths
- To ensure predictable outcomes, avoid lookup paths (e.g.,
<nixpkgs>) in production, as the output can vary depending on the version of Nixpkgs in use.
Shell Environment Example
{ pkgs ? import <nixpkgs> {} }:
let
message = "hello world";
in
pkgs.mkShellNoCC {
packages = with pkgs; [ cowsay ];
shellHook = ''
cowsay ${message}
'';
}Explanation
-
Function Definition:
- The expression defines a function that takes an attribute set as an argument.
- If the argument contains the attribute
pkgs, it will be used in the function. If not,pkgswill default to importing Nixpkgs (from<nixpkgs>) with an empty attribute set ({}).
-
Message Binding:
- The
messagevariable is bound to the string"hello world".
- The
-
mkShellNoCCFunction:- The function
mkShellNoCCis part ofpkgsand is used to declare a shell environment. - It takes an attribute set as its argument.
- The function
-
Attributes of
mkShellNoCC:packages:- A list containing
cowsayfrompkgs. - The
with pkgs;statement makespkgsattributes (likecowsay) directly accessible in this expression.
- A list containing
shellHook:- An indented string that runs when the shell environment is initialized.
- The string contains an interpolated expression,
${message}, which will expand to"hello world".
-
Result:
- When the shell environment is started, the
cowsaycommand will run with the message"hello world".
- When the shell environment is started, the
NixOS Configuration Example
{ config, pkgs, ... }: {
imports = [ ./hardware-configuration.nix ];
environment.systemPackages = with pkgs; [ git ];
# ...
}-
Function Definition:
- The expression defines a function that takes an attribute set as an argument.
- The argument set must contain at least
configandpkgs, though additional attributes (...) may be provided. - The function returns another attribute set.
-
importsAttribute:importsis an attribute containing a list with one element: the path./hardware-configuration.nix.- This file is typically part of the NixOS system configuration and handles hardware-specific settings.
- Note:
importshere is not the same as the built-inimportfunction but rather a standard attribute used in NixOS configurations.
-
environment.systemPackagesAttribute:environment.systemPackagesdefines the list of system-wide packages to install.- The
with pkgs;statement makes thepkgsattribute set accessible, allowing direct use of package names likegit. - In this case, the configuration installs
gitas a system package.
-
Unused
configArgument:- The
configargument is included in the function’s parameters but is not used in this specific example.
- The
Package Declaration Example
{ lib, stdenv, fetchurl }:
stdenv.mkDerivation rec {
pname = "hello";
version = "2.12";
src = fetchurl {
url = "mirror://gnu/${pname}/${pname}-${version}.tar.gz";
sha256 = "1ayhp9v4m4rdhjmnl2bq3cibrbqqkgjbl3s7yk2nhlh8vj3ay16g";
};
meta = with lib; {
license = licenses.gpl3Plus;
};
}-
Function Definition:
- This is a function that takes an attribute set as an argument. The set must contain:
lib(library functions)stdenv(standard environment for building packages)fetchurl(a function to fetch source files)
- This is a function that takes an attribute set as an argument. The set must contain:
-
stdenv.mkDerivation:- The function returns the result of calling
mkDerivation(a function fromstdenv). mkDerivationis used to declare a package derivation, which defines how to build the package.- The
reckeyword allows recursive references within the attribute set (i.e., attributes likepnameandversioncan refer to each other).
- The function returns the result of calling
-
Package Name and Version:
pname = "hello";: The package name is “hello”.version = "2.12";: The package version is “2.12”.
-
Source (
src):-
srcdefines the source of the package. -
The
fetchurlfunction fetches the package source from a URL:url = "mirror://gnu/${pname}/${pname}-${version}.tar.gz";- The URL is dynamically generated using
pnameandversion.
- The URL is dynamically generated using
-
The
sha256attribute ensures integrity by verifying the file’s checksum.
-
-
Metadata (
meta):metais an attribute set containing metadata for the package.- The
licenseattribute is set tolib.licenses.gpl3Plus, indicating that the package is licensed under the GPL-3.0 or later license. - The
with lib;statement allows convenient access tolibattributes likelicenses.
Understanding Nix code requires context/conventions
In the Nix language, understanding the code’s structure is often straightforward, but determining what the code actually means—such as data types or function behaviors—requires additional context. The language is driven by conventions, particularly from Nixpkgs and NixOS.
Key Challenges in Understanding Nix Code
-
Data Types:
-
It’s often unclear from code alone what the data type of a named value or function argument is.
-
Example:
{ x, y, z }: (x y) z.a- Questions:
- How do we know that
xis a function that returns another function? - How do we determine that
yis a valid argument tox? - How do we know
z.awill be a valid argument to(x y)? - Is
zan attribute set, and does it contain the attributea? - What are the data types of
yandz.a?
- How do we know that
- Questions:
-
-
Conventions Over Types:
- Unlike some languages with strict type systems, Nix doesn’t explicitly define data types in function signatures.
- Understanding the types and arguments typically requires knowing the context and conventions of the Nix ecosystem.
How the Nix Ecosystem Provides Context
-
Nixpkgs:
- Provides conventions and generic build mechanisms.
- Names commonly encountered in Nix code (like
stdenvormkDerivation) come from Nixpkgs. - Read Nix Pills
-
Common Build Mechanisms:
stdenv: IncludesmkDerivation, the core function for package derivations.- Trivial Builders: Tools for creating files and shell scripts.
-
Modifying Packages:
- Packages in Nixpkgs can be modified using:
overrideandoverrideAttrs: Modify specific packages.- overlays: Create a custom version of Nixpkgs with individually modified packages.
- Packages in Nixpkgs can be modified using:
-
- Nixpkgs provides tools to build language- or framework-specific packages.
-
NixOS Modular Configuration:
- NixOS modules: Organize and structure NixOS system configurations.
- Modules impose additional conventions specific to the NixOS Linux distribution.