Monday, July 28, 2014

Nix pill 5: functions and imports

Welcome to the fifth Nix pill. In the previous fourth pill we touched the Nix language for a moment. We introduced basic types and values of the Nix language, and basic expressions such as "if", "with" and "let". I invite you to re-read about these expressions and play with them in the repl.

Functions help to build reusable components in a big repository like nixpkgs. The Nix manual has a great explanation of functions. Let's go: pill on one hand, Nix manual on the other hand.

I remind you how to enter the Nix environment: source ~/.nix-profile/etc/profile.d/nix.sh

Nameless and single parameter


Functions are anonymous (lambdas), and only have a single parameter. The syntax is extremely simple. Type the parameter name, then ":", then the body of the function.
nix-repl> x: x*2
«lambda»
So here we defined a function that takes a parameter x, and returns x*2. The problem is that we cannot use it in any way, because it's unnamed... joke!
We can store functions in variables.
nix-repl> double = x: x*2
nix-repl> double
«lambda»
nix-repl> double 3
6
As usual, please ignore the special syntax for assignments inside nix-repl.
So, we defined a function x: x*2 that takes one parameter x, and returns x*2. This function is then assigned to the variable double.
Finally we did our first function call: double 3.
Big note: it's not like many other programming languages where you write double(3). It really is double 3.

In summary: to call a function, name the variable, then space, then the argument. Nothing else to say, it's as easy as that.

More than one parameter


How do we create a function that accepts more than one parameter? For people not used to functional programming, this may take a while to grasp. Let's do it step by step.
nix-repl> mul = a: (b: a*b)
nix-repl> mul
«lambda»
nix-repl> mul 3
«lambda»
nix-repl> (mul 3) 4
12
We defined a function that takes the parameter "a", the body returns another function. This other function takes a parameter "b" and returns a*b.
Therefore, calling "mul 3" returns this kind of function: b: 3*b. In turn, we call the returned function with 4, and get the expected result.

You don't have to use parenthesis at all, Nix has sane priorities when parsing the code:
nix-repl> mul = a: b: a*b
nix-repl> mul
«lambda»
nix-repl> mul 3
«lambda»
nix-repl> mul 3 4
12
nix-repl> mul (6+7) (8+9)
221
Much more readable, you don't even notice that functions only receive one argument.
Since the argument is separated by a space, to pass more complex expressions you need parenthesis. In other common languages you would write  mul(6+7, 8+9).

Given that functions have only one parameter, it is straightforward to use partial application:
nix-repl> foo = mul 3
nix-repl> foo 4
12
nix-repl> foo 5
15
We stored the function returned by mul 3 into a variable foo, then reused it.

Arguments set


Now this is a very cool feature of Nix. It is possible to pattern match over a set in the parameter. We write an alternative version of mul = a: b: a*b first by using a set as argument, then using pattern matching.
nix-repl> mul = s: s.a*s.b
nix-repl> mul { a = 3; b = 4; }
12
nix-repl> mul = { a, b }: a*b
nix-repl> mul { a = 3; b = 4; }
12
In the first case we defined a function that accepts a single parameter. We then access attributes "a" and "b" from the given set.
Note how the parenthesis-less syntax for function calls is very elegant in this case, instead of doing mul({ a=3; b=4; }) in other languages.

In the second case we defined an arguments set. It's like defining a set, except without values. We require that the passed set contains the keys "a" and "b". Then we can use those "a" and "b" in the function body directly.
nix-repl> mul { a = 3; b = 4; c = 6; }
error: anonymous function at (string):1:2 called with unexpected argument `c', at (string):1:1
nix-repl> mul { a = 3; }
error: anonymous function at (string):1:2 called without required argument `b', at (string):1:1
Only a set with exactly the attributes required by the function is accepted, nothing more, nothing less.

Default and variadic attributes


It is possible to specify default values of attributes in the arguments set:
nix-repl> mul = { a, b ? 2 }: a*b
nix-repl> mul { a = 3; }
6
nix-repl> mul { a = 3; b = 4; }
12
Also you can allow passing more attributes (variadic) than the expected ones:
nix-repl> mul = { a, b, ... }: a*b
nix-repl> mul { a = 3; b = 4; c = 2; }
However, in the function body you cannot access the "c" attribute. The solution is to give a name to the given set with the @-pattern:
nix-repl> mul = s@{ a, b, ... }: a*b*s.c
nix-repl> mul { a = 3; b = 4; c = 2; }
24
That's it, you give a name to the whole parameter with name@ before the set pattern.

Advantages of using argument sets:
  • Named unordered arguments: you don't have to remember the order of the arguments.
  • You can pass sets, that adds a whole new layer of flexibility and convenience. 
Disadvantages:
  • Partial application does not work with argument sets. You have to specify the whole attribute set, not part of it.
You may find similarities with Python **kwargs.

Imports


The "import" function is built-in and provides a way to parse a .nix file. The natural approach is to define each component in a .nix file, then compose by importing these files.

Let's start with the bare metal.

a.nix:
3
b.nix:
4
mul.nix:
a: b: a*b
nix-repl> a = import ./a.nix
nix-repl> b = import ./b.nix
nix-repl> mul = import ./mul.nix
nix-repl> mul a b
12
Yes it's really that straight. You import a file, and it gets parsed as expression. Note that the scope of the imported file does not inherit the scope of the importer.

test.nix:
x
nix-repl> let x = 5; in import ./test.nix
error: undefined variable `x' at /home/lethal/test.nix:1:1
So how do we pass information to the module? Use functions, like we did with mul.nix .
A more complex example:

test.nix:
{ a, b ? 3, trueMsg ? "yes", falseMsg ? "no" }:
if a > b
  then builtins.trace trueMsg true
  else builtins.trace falseMsg false
nix-repl> import ./test.nix { a = 5; trueMsg = "ok"; }
trace: ok
true
Explaining:
  • In test.nix we return a function. It accepts a set, with default attributes b, trueMsg and falseMsg.
  • builtins.trace is a built-in function that takes two arguments. The first is the message to display, the second is the value to return. It's usually used for debugging purposes.
  • Then we import test.nix, and call the function with that set.
So when is the message shown? Only when it's in need to be evaluated.

Next pill


...we will finally write our first derivation.

Nix pill 6 is available for reading here.

To be notified about the new pill, stay tuned on #NixPills, follow @lethalman or subscribe to the nixpills rss.

13 comments:

Anonymous said...

luca, I guess the links to "anchors" in the nix[os] manual are based on auto-generated random IDs which are not stable when the manual gets updated, so many of them are broken.

thanks for the tutorial, pal!

Arunvijay said...

Good post....
Coronavirus Update
Intern Ship In Chennai
Inplant Training In Chennai
Internship For CSE Students
Online Internships
Internship For MBA Students
ITO Internship

Lord Of The Maverick said...

kokteyl catering
kokteyl catering fiyatları
istanbul kokteyl catering
catering kokteyl menüleri
fuar yemek organizasyon
fuar yemek organizasyo firmaları
fuar için yemek firmaları
düğün yemek organizasyonu
düğün yemek organizasyonu yapan firmalar
istanbul kokteyl catering
istanbul kokteyl catering firmaları
Kokteyl catering fiyatları
istanbul catering firmaları listesi
istanbul catering şirketleri
istanbul Fuar catering Hizmetleri
catering şirketleri istanbul
istanbul daki catering firmaları
300 kişilik yemek Fiyatları
istanbul fuar yemek organizasyonn
istanbul fuar yemek organizasyon firmaları
istanbul fuar için yemek firmaları

Birol Sarıteke said...

Sağlık Haberleri Sağlıklı Yaşam ve Beslenme Sağlık alanından en son araştırmalar, sağlıklı yaşam için gereklilikler, diyetler, beslenme şekilleri, hastalıklar ve yeni tedavi yöntemleri hakkında güncel bilgiler.

pandith13 said...

Great post, thanks
SRI ANNAPOORNESHAWARI ASTROLOGY CENTER.Best Astrologer In Pennsylvania

shri chakram astro centre said...

Nice post. Keep sharing. Thanks for sharing.

SRICHAKRAM ASTROLOGY.Best Astrologer In Hebbal


Pandith13 said...

it was great information and very useful

SRI ANNAPOORNESHAWARI ASTROLOGY CENTER.Best Astrologer In Navi Mumbai

Vasudeva said...

Great post, thanks

SRIKRISHANA ASTROLOGY.Best Astrologer In Raichur

DurgaAnugarha said...

it was great information and very useful

DURGAANUGARHA ASTROLOGY.Best Astrologer In kr-puram

abhiramindia said...

Thank you for your post. This is excellent information

ABHIRAM ASTROLOGY CENTER.Best Astrologer In northwest-territories

Macroeconomicsassignmenthelp said...

We produce the best academic assignments and bespoke Macroeconomics assignment help for every academic stream. We take pride in our highly qualified and experienced subject experts onboard and hence guarantee you the best academic grade for each of your assignments.

Online Psikolog Platform Bloğu said...


Sancaktepe Online Psikolog

İstanbul Online Psikolog



istanbul terapist

Germencik Beko said...

Germencik Beko Mağazası, evinizin ihtiyaçlarını karşılayacak geniş bir ürün yelpazesi sunar. Mutfakta kullanabileceğiniz son teknoloji fırınlar, ocaklar ve buzdolapları, aynı zamanda çamaşır makineleri, bulaşık makineleri ve kurutma makineleri gibi beyaz eşyaların yanı sıra televizyonlar, klimalar ve daha fazlası mağazamızda bulunabilir. Her bütçeye uygun seçenekler sunarak, herkesin ihtiyaçlarını karşılamayı amaçlıyoruz.

www.aydinbeko.com.tr
Germencik Beko
Aydin Beko
Efeler Beko