mirror of
https://codeberg.org/mhwombat/nix-book.git
synced 2025-12-27 00:34:58 +08:00
164 lines
4.4 KiB
Text
164 lines
4.4 KiB
Text
[#functions]
|
|
= Functions
|
|
|
|
== Anonymous functions
|
|
|
|
Functions are defined using the syntax `_parameter_: _expression_`,
|
|
where the _expression_ typically involves the _parameter_.
|
|
Consider the following example.
|
|
|
|
[source]
|
|
.Example
|
|
....
|
|
nix-repl> x: x + 1
|
|
«lambda @ «string»:1:1»
|
|
....
|
|
|
|
We created a function that adds `1` to its input.
|
|
However, it doesn't have a name, so we can't use it directly.
|
|
Anonymous functions do have their uses, as we shall see shortly.
|
|
|
|
// TODO Add a cross-reference to "shortly"
|
|
// TODO Show that you can pass a function to a function
|
|
|
|
Note that the message printed by the Nix REPL when we created the function uses the term _lambda_.
|
|
This derives from a branch of mathematics called _lambda calculus_.
|
|
Lambda calculus was the inspiration for most functional languages such as Nix.
|
|
Functional programmers often call anonymous functions "lambdas".
|
|
|
|
The Nix REPL confirms that the expression `x: x + 1` defines a function.
|
|
|
|
[source]
|
|
.Example
|
|
....
|
|
nix-repl> :t x: x + 1
|
|
a function
|
|
....
|
|
|
|
== Named functions and function application
|
|
|
|
How can we use a function?
|
|
Recall from <<type-lambda>> that functions can be treated like any other data type.
|
|
In particular, we can assign it to a variable.
|
|
|
|
[source]
|
|
.Example
|
|
....
|
|
nix-repl> f = x: x + 1
|
|
|
|
nix-repl> f
|
|
«lambda @ «string»:1:2»
|
|
....
|
|
|
|
// TODO Show that you can pass a function to a function
|
|
|
|
Procedural languages such as C or Java often use parenthesis to apply a function to a value, e.g. `f(5)`.
|
|
Nix, like lambda calculus and most functional languages, does not require parenthesis for function application.
|
|
This reduces visual clutter when chaining a series of functions.
|
|
|
|
Now that our function has a name, we can use it.
|
|
|
|
[source]
|
|
.Example
|
|
....
|
|
nix-repl> f 5
|
|
6
|
|
....
|
|
|
|
== Multiple parameters using nested functions
|
|
|
|
Functions in Nix always have a single parameter.
|
|
To define a calculation that requires more than one parameter,
|
|
we create functions that return functions!
|
|
|
|
[source]
|
|
.Example
|
|
....
|
|
nix-repl> add = a: (b: a+b)
|
|
....
|
|
|
|
We have created a function called `add`.
|
|
When applied to a parameter `a`, it returns a new function that adds `a` to its input.
|
|
Note that the expression `(b: a+b)` is an anonymous function.
|
|
We never call it directly, so it doesn't need a name.
|
|
Anonymous functions are useful after all!
|
|
|
|
I used parentheses to emphasise the inner function, but they aren't necessary.
|
|
More commonly we would write the following.
|
|
|
|
[source]
|
|
.Example
|
|
....
|
|
nix-repl> add = a: b: a+b
|
|
....
|
|
|
|
If we only supply one parameter to `add`, the result is a new function rather than a simple value.
|
|
Invoking a function without supplying all of the expected parameters is called _partial application_.
|
|
|
|
[source]
|
|
.Example
|
|
....
|
|
nix-repl> add 3 # Returns a function that adds 3 to any input
|
|
«lambda @ «string»:1:6»
|
|
....
|
|
|
|
Now let's apply `add 3` to the value `5`.
|
|
|
|
[source]
|
|
.Example
|
|
....
|
|
nix-repl> (add 3) 5
|
|
8
|
|
....
|
|
|
|
In fact, the parentheses aren't needed.
|
|
|
|
[source]
|
|
.Example
|
|
....
|
|
nix-repl> add 3 5
|
|
8
|
|
....
|
|
|
|
If you've never used a functional programming language, this all probably seems very strange.
|
|
Imagine that you want to add two numbers, but you have a very unusual calculator labeled "add".
|
|
This calculator never displays a result, it only produces more calculators!
|
|
If you enter the value `3` into the "add" calculator, it gives you a second calculator labeled "add 3".
|
|
You then enter `5` into the "add 3" calculator, which displays the result of the addition, `8`.
|
|
|
|
With that image in mind, let's walk through the steps again in the REPL, but this time in more detail.
|
|
The function `add` takes a single parameter `a`,
|
|
and returns a new function that takes a single parameter `b`, and returns the value `a + b`.
|
|
Let's apply `add` to the value `3`, and give the resulting new function a name, `g`.
|
|
|
|
[source]
|
|
.Example
|
|
....
|
|
nix-repl> g = add 3
|
|
....
|
|
|
|
The function `g` takes a single parameter and adds `3` to it.
|
|
The Nix REPL confirms that `g` is indeed a function.
|
|
|
|
[source]
|
|
.Example
|
|
....
|
|
nix-repl> :t g
|
|
a function
|
|
....
|
|
|
|
Now we can apply `g` to a number to get a new number.
|
|
|
|
[source]
|
|
.Example
|
|
....
|
|
nix-repl> g 5
|
|
8
|
|
....
|
|
|
|
== Multiple parameters using attribute sets
|
|
|
|
I said earlier that a function in Nix always has a single parameter.
|
|
However, that parameter need not be a simple value; it could be a list or an attribute set.
|
|
This approach is widely used in Nix, and the language has some special features to support it.
|
|
This is an important topic, so we will cover it separately in <<argument-sets>>.
|