Understanding_nix_functions
Understanding Nix Functions

Functions are a fundamental concept in Nix and are prevalent throughout Nix code. Grasping how they work is crucial for understanding and writing Nix expressions.
The Single-Argument Nature of Nix Functions
A key concept to understand is that in Nix, every function conceptually takes exactly one argument. What might appear as multi-argument functions are actually achieved through a technique called currying, where a series of nested single-argument functions are used.
Identifying Function Structure The Colon
The colon (:
) acts as a clear separator within a function definition:
- Left of the Colon: This is the function’s argument. It’s a placeholder name for a value that will be provided when the function is called.
- Right of the Colon: This is the function body. It’s the expression that will be evaluated when the function is invoked.
Think of function arguments as naming values that aren’t known in advance. These names are placeholders that get filled with specific values when the function is used.
Example:
greet = personName: "Hello, ${personName}!";
Here,
personName
is the argument (the placeholder)."Hello, ${personName}!"
, is the function body (the expression that uses the placeholder).
When you call the function:
greet "Anonymous" # Evaluates to "Hello, Anonymous!"
The value "Anonymous"
is substituted for the personName
placeholder within
the function body.
Function Declarations Single and “Multiple” Arguments
Single-Argument Functions
The simplest form of a Nix function takes a single argument:
inc = x: x + 1;
inc 5 # Evaluates to 6
x
is the argument.x + 1
is the function body.
Simulating Multiple Arguments: Currying
To create functions that appear to take multiple arguments, Nix uses currying. This involves nesting single-argument functions, where each function takes one argument and returns another function that takes the next argument, and so on.
concat = x: y: x + y;
concat 6 6 # Evaluates to 12
Nix interprets the colons as separators for this chain of single-argument functions.
Understanding the Chain:
Consider the greeting
function:
greeting = prefix: name: "${prefix}, ${name}!";
This is effectively a chain:
- Outer Function:
prefix: (name: "${prefix}, ${name}!")
Takes one argument:
prefix
.Its body is another function definition: name:
"${prefix}, ${name}!"
.
2 Inner Function: name: "${prefix}, ${name}!"
Takes one argument:
name
.Its body uses both its own argument (
name
) and the argument from the outer function’s scope (prefix).
Step-by-Step Evaluation:
When you call greeting "Hello" "Alice"
:
greeting "Hello"
:
The
greeting
function is called with"Hello"
as theprefix
.The outer function returns the inner function:
name: "Hello, ${name}!"
(whereprefix
is now fixed as `“Hello”`` in its scope).
(greeting "Hello") "Alice"
:
The resulting inner function is then called with
"Alice"
as thename
.The inner function evaluates its body:
"Hello, ${"Alice"}!"
, resulting in"Hello, Alice!"
.
Key Insight: Every colon in a function definition separates a single argument from its function body, even if that body is another function definition.
Partial Application: Using Functions Incrementally
Because of currying, you can apply arguments to a Nix function one at a time. This is called partial application. When you provide only some of the expected arguments, you get a new function that “remembers” the provided arguments and waits for the rest.
Example:
Using our greeting
function again:
greeting = prefix: name: "${prefix}, ${name}!";
If we only provide the prefix:
helloGreeting = greeting "Hello";
helloGreeting
is now a new function. It has already received theprefix
argument ("Hello"
) and is waiting for thename
argument.
Calling helloGreeting
:
helloGreeting "Sally" # Evaluates to "Hello, Sally!"
Benefits of Partial Application:
Creating Specialized Functions: You can create more specific functions from general ones by fixing some of their parameters.
Adapting to Higher-Order Functions: Many functions that operate on other functions (like
map
andfilter
) expect functions with a certain number of arguments. Partial application allows you to adapt existing functions to fit these requirements.
The Function Nature of NixOS and Home Manager Modules
It’s crucial to understand that most NixOS and Home Manager modules are fundamentally functions.
- These module functions typically accept a single argument: an attribute set.
Example:
A simplified Nginx service module:
{ config, lib, pkgs, ... }: {
services.nginx.enable = true;
services.nginx.package = pkgs.nginx;
services.nginx.settings."http-port" = "8080";
}
The entire module definition is a function that takes one argument:
{ config, lib, pkgs, ... }
.When this module is included in your configuration, the NixOS module system calls this function with a specific attribute set. This attribute set contains the current system configuration (
config
), the Nix standard library (lib
), the available packages (pkgs
), and other relevant information. The module then uses these values to define parts of your system.