Skip to Content

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.
  • 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.
  • Developer Tools:

    • Tools for testing, debugging, linting, formatting, etc.
  • Generic Build Mechanisms:

    • Includes stdenv.mkDerivation, trivial builders, and more.
  • Composition and Configuration Mechanisms:

    • Tools like override, overrideAttrs, overlays, callPackage, etc.
  • Ecosystem-Specific Packaging Mechanisms:

    • Packaging methods such as buildGoModule, buildPythonApplication, etc.
  • NixOS Module System:

    • Consists of config, option, and other components.
  • 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 .nix extension) 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 :p before 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 :q to exit the Nix REPL.

Evaluating Nix Files

  • Use nix-instantiate --eval to 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 --eval will try to read from default.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 --strict option.
  • 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 rec allows accessing attributes from within the set.

    • Example:

      rec { one = 1; two = one + 1; three = two + 1; }
      • Output: { one = 1; two = 2; three = 3; }
  • 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'

Let Expressions (let ... in ...)

  • Also called let binding, this allows assigning names to values for repeated use within a specific scope.

  • Basic Structure:

    • let introduces the assignments, followed by in, where the expression using those assignments is defined.

    • Example:

      let a = 1; in a + a
      • Output: 2
  • 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
  • Local Scope of Let Bindings:

    • Names declared within the let expression 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
  • 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
  • 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; }; }; }

with ...; ... Expression

  • The with expression 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 ]
    • The above is equivalent to:

      [ a.x a.y a.z ]
  • Scope of with:

    • Attributes made available via with are only in scope for the expression following the semicolon (;).
  • Counter-example:

    • Attempting to access an attribute outside the with scope 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

  • inherit is 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; }
    • This is equivalent to:

      { x = x; y = y; }
  • inherit from 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; }
    • This is equivalent to:

      { x = a.x; y = a.y; }
  • inherit in let Expressions:

    • inherit can also be used inside let expressions.

    • Example:

      let inherit ({ x = 1; y = 2; }) x y; in [ x y ]
      • Output: [ 1 2 ]
    • 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"
  • 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"
  • 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"

File System Paths in Nix

  • Absolute paths always start with a slash (/).

    • Example:

      /absolute/path
      • Output: /absolute/path
  • 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
    • Example:

      relative/path
      • Output: /current/directory/relative/path
  • Dot Notation for Current Directory:

    • A single dot (.) represents the current directory in a path.

    • Example:

      ./.
      • Output: /current/directory
    • 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
  • 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
    • Accessing a subdirectory:

      <nixpkgs/lib>
      • Output: /nix/var/nix/profiles/per-user/root/channels/nixpkgs/lib
  • 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"
  • 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"

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:

    1. Single Argument:

      • Example:

        x: x + 1
      • This creates a function that adds 1 to x.

    2. Multiple Arguments (via nesting):

      • Example:

        x: y: x + y
      • This is a function that takes two arguments (x and y) and adds them together.

    3. Attribute Set Argument:

      • Example:

        { a, b }: a + b
      • This function takes an attribute set with a and b, and adds their values.

    4. Attribute Set with Default Values:

      • Example:

        { a, b ? 0 }: a + b
      • This function sets b to 0 if not provided.

    5. Attribute Set with Additional Attributes Allowed:

      • Example:

        { a, b, ... }: a + b
      • This allows additional attributes besides a and b.

    6. 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).

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
  • Calling a Function on an Attribute Set:

    • Example:

      let f = x: x.a; in f { a = 1; }
      • Output: 1
  • Passing Arguments by Name:

    • You can also pass named arguments:

      let f = x: x.a; v = { a = 1; }; in f v
      • Output: 1
  • Using Parentheses for Clarity:

    • Parentheses are sometimes required to distinguish the function from the argument:

      (x: x + 1) 1
      • Output: 2
  • Function Application vs. List Construction:

    • Example 1: Applying f to a and 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 of f a).
    • Example 2: Placing f and a as separate elements in a list:

      let f = x: x + 1; a = 1; in [ f a ]
      • Output: [ <LAMBDA> 1 ] (a list with two elements: the function f and the argument a).

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: a and b.

    • Example usage:

      let f = {a, b}: a + b; in f { a = 1; b = 2; }
      • Output: 3
  • 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 (since b defaults to 0).
  • Example with Multiple Default Values:

    let f = {a ? 0, b ? 0}: a + b; in f { } # Empty attribute set
    • Output: 0 (both a and b default to 0).
  • 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 attribute c is ignored, but doesn’t cause an error).

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 (since a = 1, b = 2, and args.c = 3).

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 builtins constant.

  • Example:

    builtins.toString
    • Output: <PRIMOP> (indicates a built-in function).

import

  • import is a top-level function (not under builtins).

  • It reads a path to a Nix file, evaluates the contained expression, and returns the result.

  • If the path is a directory, import looks for default.nix in that directory.

  • Example:

    echo 1 + 2 > file.nix
    import ./file.nix
    • Output: 3 (the expression 1 + 2 is evaluated from file.nix).
  • Using import for Functions:

    • If a Nix file contains a function, it can be imported and immediately applied to arguments.

    • Example:

      echo "x: x + 1" > file.nix
      import ./file.nix 1
      • Output: 2 (the function x: x + 1 is applied to 1).
  • 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 (unlike builtins, 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

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

Using pkgs as a Function Argument

  • You may see pkgs passed 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"

Direct Use of lib as an Argument

  • Sometimes, lib is passed directly instead of pkgs:

    { 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

When Both pkgs and lib Are Passed

  • In some cases, both pkgs and lib are passed as arguments to avoid repeated use of pkgs.lib:

    { pkgs, lib, ... }: # Use both pkgs and lib

Historical Functions in pkgs.lib

  • Some functions in pkgs.lib are equivalent to builtins with 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/store when 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"
  • 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.fetchurl
    • builtins.fetchTarball
    • builtins.fetchGit
    • builtins.fetchClosure
  • Example of fetching a file:

    builtins.fetchurl "https://github.com/NixOS/nix/archive/7c3ab5751568a0bc63430b33a5169c5e4784a0ff.tar.gz"
    • Output: "/nix/store/7dhgs330clj36384akg86140fqkgh8zf-7c3ab5751568a0bc63430b33a5169c5e4784a0ff.tar.gz"
  • Example of fetching and unpacking a tarball:

    builtins.fetchTarball "https://github.com/NixOS/nix/archive/7c3ab5751568a0bc63430b33a5169c5e4784a0ff.tar.gz"
    • Output: "/nix/store/d59llm96vgis5fy231x6m7nrijs0ww36-source"
  • 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 derivation function.

    • However, this is typically wrapped by stdenv.mkDerivation in Nixpkgs to simplify complex build procedures.
    • mkDerivation denotes something Nix will eventually build.

Example of a Package Using mkDerivation:

  • The result of derivation (and mkDerivation) 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:

    • mkDerivation simplifies 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

  1. 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, pkgs will default to importing Nixpkgs (from <nixpkgs>) with an empty attribute set ({}).
  2. Message Binding:

    • The message variable is bound to the string "hello world".
  3. mkShellNoCC Function:

    • The function mkShellNoCC is part of pkgs and is used to declare a shell environment.
    • It takes an attribute set as its argument.
  4. Attributes of mkShellNoCC:

    • packages:
      • A list containing cowsay from pkgs.
      • The with pkgs; statement makes pkgs attributes (like cowsay) directly accessible in this expression.
    • 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".
  5. Result:

    • When the shell environment is started, the cowsay command will run with the message "hello world".

NixOS Configuration Example

{ config, pkgs, ... }: { imports = [ ./hardware-configuration.nix ]; environment.systemPackages = with pkgs; [ git ]; # ... }
  1. Function Definition:

    • The expression defines a function that takes an attribute set as an argument.
    • The argument set must contain at least config and pkgs, though additional attributes (...) may be provided.
    • The function returns another attribute set.
  2. imports Attribute:

    • imports is 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:
      • imports here is not the same as the built-in import function but rather a standard attribute used in NixOS configurations.
  3. environment.systemPackages Attribute:

    • environment.systemPackages defines the list of system-wide packages to install.
    • The with pkgs; statement makes the pkgs attribute set accessible, allowing direct use of package names like git.
    • In this case, the configuration installs git as a system package.
  4. Unused config Argument:

    • The config argument is included in the function’s parameters but is not used in this specific example.

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; }; }
  1. 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)
  2. stdenv.mkDerivation:

    • The function returns the result of calling mkDerivation (a function from stdenv).
    • mkDerivation is used to declare a package derivation, which defines how to build the package.
    • The rec keyword allows recursive references within the attribute set (i.e., attributes like pname and version can refer to each other).
  3. Package Name and Version:

    • pname = "hello";: The package name is “hello”.
    • version = "2.12";: The package version is “2.12”.
  4. Source (src):

    • src defines the source of the package.

    • The fetchurl function fetches the package source from a URL:

      url = "mirror://gnu/${pname}/${pname}-${version}.tar.gz";
      • The URL is dynamically generated using pname and version.
    • The sha256 attribute ensures integrity by verifying the file’s checksum.

  5. Metadata (meta):

    • meta is an attribute set containing metadata for the package.
    • The license attribute is set to lib.licenses.gpl3Plus, indicating that the package is licensed under the GPL-3.0 or later license.
    • The with lib; statement allows convenient access to lib attributes like licenses.

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

  1. 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 x is a function that returns another function?
        • How do we determine that y is a valid argument to x?
        • How do we know z.a will be a valid argument to (x y)?
        • Is z an attribute set, and does it contain the attribute a?
        • What are the data types of y and z.a?
  2. 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 stdenv or mkDerivation) come from Nixpkgs.
    • Read Nix Pills 
  • Common Build Mechanisms:

    • stdenv: Includes mkDerivation, the core function for package derivations.
    • Trivial Builders: Tools for creating files and shell scripts.
  • Modifying Packages:

    • Packages in Nixpkgs can be modified using:
      • override and overrideAttrs: Modify specific packages.
      • overlays: Create a custom version of Nixpkgs with individually modified packages.
  • Language-Specific Mechanisms:

    • 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.
Last updated on