diff --git a/pills/05-functions-and-imports.xml b/pills/05-functions-and-imports.xml index d15ef59..0cb59c7 100644 --- a/pills/05-functions-and-imports.xml +++ b/pills/05-functions-and-imports.xml @@ -1,8 +1,7 @@ +xmlns:xlink="http://www.w3.org/1999/xlink" +xmlns:xi="http://www.w3.org/2001/XInclude" version="5.0" +xml:id="functions-and-imports"> functions and imports @@ -10,15 +9,26 @@ 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. + linkend="basics-of-the-language">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. + 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 + I remind you how to enter the Nix environment: source + ~/.nix-profile/etc/profile.d/nix.sh @@ -27,31 +37,46 @@ 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. + 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. - 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! + 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. - + - 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. + 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. + 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. + In summary: to call a function, name the variable, then space, then + the argument. Nothing else to say, it's as easy as that. @@ -59,33 +84,49 @@ 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. + 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. - + - 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. + 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: + You don't have to use parenthesis at all, Nix has sane priorities + when parsing the code: - + - 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). + 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: + Given that functions have only one parameter, it is straightforward + to use partial application: - + - We stored the function returned by mul 3 into a variable foo, then reused it. + We stored the function returned by mul 3 into a + variable foo, then reused it. @@ -93,23 +134,37 @@ 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. + 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. - 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 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. + 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. - + - Only a set with exactly the attributes required by the function is accepted, nothing more, nothing less. + Only a set with exactly the attributes required by the function is + accepted, nothing more, nothing less. @@ -117,25 +172,33 @@ Default and variadic attributes - It is possible to specify default values of attributes in the arguments set: + It is possible to specify default + values of attributes in the arguments set: - + - Also you can allow passing more attributes (variadic) than the expected ones: + Also you can allow passing more attributes (variadic) than the expected ones: - + - 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: + 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: - + - That's it, you give a name to the whole parameter with name@ before the set pattern. + That's it, you give a name to the whole parameter with name@ before + the set pattern. @@ -145,12 +208,14 @@ - Named unordered arguments: you don't have to remember the order of the arguments. + 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. + You can pass sets, that adds a whole new layer of + flexibility and convenience. @@ -162,13 +227,17 @@ - Partial application does not work with argument sets. You have to specify the whole attribute set, not part of it. + 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. + You may find similarities with Python + **kwargs. @@ -176,7 +245,10 @@ 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. + 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. @@ -204,7 +276,9 @@ - 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. + 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. @@ -216,7 +290,8 @@ - So how do we pass information to the module? Use functions, like we did with mul.nix. A more complex example: + So how do we pass information to the module? Use functions, like we + did with mul.nix. A more complex example: @@ -233,23 +308,32 @@ - In test.nix we return a function. It accepts a set, with default attributes b, trueMsg and falseMsg. + 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. + 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. + 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. + So when is the message shown? Only when it's in need to be + evaluated.