Depending or your level of experience with Nix, anonymous functions, the tendency to call the latter lambdas, etc.; the above advice might range from spot-on-helpful to totally useless (or even worse, by raising more confusing questions than it answers).
So, in case it helps you, or anyone else in the future, here is an explanation of what it means.
Lambda
Firlstly, as you’re writing in Python, chances are you know what a lambda is in general, but you might not appreciate what they look like in Nix. The following are equivalent
lambda a: a + 1 # Python
a: a + 1 # Nix
They are both anonymous functions with one parameter a
which return a + 1
when they are called. The top one is in Python, the bottom one in Nix.
(In other languages these might look like
|a| a + 1
[](int a) {return a+1;}
\a -> a + 1
(lambda (a) (+ a 1))
etc., etc., etc. but they all mean the same thing (modulo polymorphism))
Hopefully this clears up why the advice talks about ‘the lambda’ while ‘lambda’ does not appear anywhere in the code.
Role of parentheses in Nix
The way you write withPackages(ps: ...)
with the opening-parenthesis right up against the function, suggests that you maybe think that the parentheses play the role of the function call syntax, like they do in Python (and many other languages). Beware! this is not the case in Nix!
This is how to call a function fn
with two arguments a
and b
, first in Python, then in Nix
fn(a, b) # Python
fn a b # Nix
I recommend that you change the style in which you write function calls in Nix, putting a space between the function and its argument, even if the argument is in parentheses. That should help you remember that the role of parentheses is to group things together rather than calling functions.
(A discussion of currying would probably be more confusing than helpful to the majority of the target audience at this point, so I’ll leave it out.)
Question
What is the meaning of
fn(a b)
in Nix?
Answer
- First, call the function
a
with the argument b
…
- then call the function
fn
with the result of the previous step
Moral of the story: the parentheses are there for explicit precedence, not as a part of the function call syntax.
To someone used to parsing C-family languages, this can be very misleading: you might have to work hard to suppress your natural parsing instincts when reading and writing Nix!
Make sure you understand these:
fn a b # Call `fn` with two arguments
fn (a b) # Call `fn` with one argument (the result of calling `a` with one argument)
fn a # Call `fn` with one argument
fn(a) # Exactly the same as the previous line!
Nix list syntax
Note that the Nix list syntax, unlike Python (and many other languages), does not use commas to separate elements. List elements are separated by whitespace (… or parentheses!). But function arguments are also separated by whitespace … so if you put a function call inside a list, who wins? Which syntax claims the spaces (… or parentheses)?
You might be surprised by the answer:
[1 2 3] # 3 integers in a list
[fn a b] # 3 elements in a list
[fn (a b)] # 2 elements in ...
[fn(a b)] # 2 elements !!!!!! (Don't be fooled by your Python parsing habits!)
[fn a] # 2 elements
[fn(a)] # 2 elements !!!!!!
[(fn a b)] # 1 element (compare to `[fn(a b)]`, three lines up)
[(fn a)] # 1 element, c.f. `[fn(a)]` two lines up
What’s this got to do with the original problem?
Armed with this knowledge, let’s look at your problem. When you wrote
buildInputs = [python3.withPackages(ps: with ps; [ numpy libgenApi ])]
Nix parsed it as
buildInputs = [
python3.withPackages # first element in the list
(ps: with ps; [ numpy libgenApi ]) # second element in the list
]
- element 1: named function
python3.withPackages
- element 2: anonymous function (lambda)
ps: with ps; [ numpy libgenApi ]
So we should now be able to fully understand @jtojnar’s statement that
python3.withPackages
and the lambda are separate list elements.
and, hopefully, now we also understand that we wrote something of the form [fn(a)]
when we meant [(fn a)]
.
A bit more explicitly, we had something of the form
[fn(parameter: body)] # list with 2 elements: 1. named function, 2. lambda
where we need
[(fn (parameter: body))] # list with one element: the result of calling fn with a lambda as its sole argument
Conclusion
I hope that this serves to clear up a possible source of confusion in Nix, for someone.