summaryrefslogtreecommitdiffhomepage
path: root/nix.html.markdown
diff options
context:
space:
mode:
authorChris Martin <ch.martin@gmail.com>2016-03-02 18:27:01 -0800
committerChris Martin <ch.martin@gmail.com>2016-03-02 18:27:01 -0800
commitb6fd0657ea5d0bbc8deb3f1865bbdc008ee294d4 (patch)
tree6e4c7fa4822e8365abf0ea7305e209abcc4b203f /nix.html.markdown
parent8799f11b89c947924e56e61990769a3831631394 (diff)
Initial Nix tutorial
Diffstat (limited to 'nix.html.markdown')
-rw-r--r--nix.html.markdown354
1 files changed, 354 insertions, 0 deletions
diff --git a/nix.html.markdown b/nix.html.markdown
new file mode 100644
index 00000000..2979098f
--- /dev/null
+++ b/nix.html.markdown
@@ -0,0 +1,354 @@
+---
+language: nix
+filename: learn.nix
+contributors:
+ - ["Chris Martin", "http://chris-martin.org/"]
+---
+
+Nix is a simple functional language developed for the
+[Nix package manager](https://nixos.org/nix/) and
+[NixOS](https://nixos.org/).
+
+You can evaluate Nix expressions using
+[nix-instantiate](https://nixos.org/nix/manual/#sec-nix-instantiate)
+or [`nix-repl`](https://github.com/edolstra/nix-repl).
+
+```nix
+with builtins; [
+
+ # Comments
+ #=========================================
+
+ # Inline comments look like this.
+
+ /* Multi-line comments
+ look like this. */
+
+
+ # Booleans
+ #=========================================
+
+ (true && false) # And
+ #=> false
+
+ (true || false) # Or
+ #=> true
+
+ (if 3 < 4 then "a" else "b") # Conditional
+ #=> "a"
+
+
+ # Integers
+ #=========================================
+
+ # Integers are the only numeric type.
+
+ 1 0 42 (-3) # Some integers
+
+ (4 + 6 + 12 - 2) # Addition
+ #=> 20
+
+ (7 / 2) # Division
+ #=> 3
+
+
+ # Strings
+ #=========================================
+
+ "Strings literals are in double quotes."
+
+ "
+ String literals can span
+ multiple lines.
+ "
+
+ ''
+ This is called an "indented string" literal.
+ It intelligently strips leading whitespace.
+ ''
+
+ ''
+ a
+ b
+ ''
+ #=> "a\n b"
+
+ ("ab" + "cd") # String concatenation
+ #=> "abcd"
+
+ # Antiquotation lets you embed values into strings.
+ ("Your home directory is ${getEnv "HOME"}")
+ #=> "Your home directory is /home/alice"
+
+
+ # Paths
+ #=========================================
+
+ # Nix has a primitive data type for paths.
+ /tmp/tutorials/learn.nix
+
+ # A relative path is resolved to an absolute path at parse
+ # time, relative to the file in which it occurs.
+ tutorials/learn.nix
+ #=> /the-base-path/tutorials/learn.nix
+
+ # A path must contain at least one slash, so a relative
+ # path for a file in the same directory needs a ./ prefix,
+ ./learn.nix
+ #=> /the-base-path/learn.nix
+
+ # The / operator must be surrounded by whitespace if
+ # you want it to signify division.
+
+ 7/2 # This is a path literal
+ (7 / 2) # This is integer division
+
+
+ # Imports
+ #=========================================
+
+ # A nix file contains a single top-level expression with no free
+ # variables. An import expression evaluates to the value of the
+ # file that it imports.
+ (import /tmp/foo.nix)
+
+ # Imports can also be specified by strings.
+ (import "/tmp/foo.nix")
+
+ # Import paths must be absolute. Path literals
+ # are automatically resolved, so this is fine.
+ (import ./foo.nix)
+
+ # But this does not happen with strings.
+ (import "./foo.nix")
+ #=> error: string ‘foo.nix’ doesn't represent an absolute path
+
+
+ # Let
+ #=========================================
+
+ # `let` blocks allow us to bind values to variables.
+ (let x = "a"; in
+ x + x + x)
+ #=> "aaa"
+
+ # Bindings can refer to each other, and their order does not matter.
+ (let y = x + "b";
+ x = "a"; in
+ y + "c")
+ #=> "abc"
+
+ # Inner bindings shadow outer bindings.
+ (let a = 1; in
+ let a = 2; in
+ a)
+ #=> 2
+
+
+ # Functions
+ #=========================================
+
+ (n: n + 1) # Function that adds 1
+
+ ((n: n + 1) 5) # That same function, applied to 5
+ #=> 6
+
+ # There is no syntax for named functions, but they
+ # can be bound by `let` blocks like any other value.
+ (let succ = (n: n + 1); in succ 5)
+ #=> 6
+
+ # A function has exactly one argument.
+ # Multiple arguments can be achieved with currying.
+ ((x: y: x + "-" + y) "a" "b")
+ #=> "a-b"
+
+ # We can also have named function arguments,
+ # which we'll get to later after we introduce sets.
+
+
+ # Lists
+ #=========================================
+
+ # Lists are denoted by square brackets.
+
+ (length [1 2 3 "x"])
+ #=> 4
+
+ ([1 2 3] ++ [4 5])
+ #=> [1 2 3 4 5]
+
+ (concatLists [[1 2] [3 4] [5]])
+ #=> [1 2 3 4 5]
+
+ (head [1 2 3])
+ #=> 1
+ (tail [1 2 3])
+ #=> [2 3]
+
+ (elemAt ["a" "b" "c" "d"] 2)
+ #=> "c"
+
+ (elem 2 [1 2 3])
+ #=> true
+ (elem 5 [1 2 3])
+ #=> false
+
+ (filter (n: n < 3) [1 2 3 4])
+ #=> [ 1 2 ]
+
+
+ # Sets
+ #=========================================
+
+ # A "set" is an unordered mapping with string keys.
+ { foo = [1 2]; bar = "x"; }
+
+ # The . operator pulls a value out of a set.
+ { a = 1; b = 2; }.a
+ #=> 1
+
+ # The // operator merges two sets.
+ ({ a = 1; } // { b = 2; })
+ #=> { a = 1; b = 2; }
+
+ # Values on the right override values on the left.
+ ({ a = 1; b = 2; } // { a = 3; c = 4; })
+ #=> { a = 3; b = 2; c = 4; }
+
+ # The rec keyword denotes a "recursive set",
+ # in which attributes can refer to each other.
+ (let a = 1; in { a = 2; b = a; }.b)
+ #=> 1
+ (let a = 1; in rec { a = 2; b = a; }.b)
+ #=> 2
+
+ # Nested sets can be defined in a piecewise fashion.
+ {
+ a.b = 1;
+ a.c.d = 2;
+ a.c.e = 3;
+ }.a.c
+ #=> { d = 2; e = 3; }
+
+ # An attribute's descendants cannot be assigned in this
+ # way if the attribute itself has been directly assigned.
+ {
+ a = { b = 1; };
+ a.c = 2;
+ }
+ #=> error: attribute ‘a’ already defined
+
+
+ # With
+ #=========================================
+
+ # The body of a `with` block is evaluated with
+ # a set's mappings bound to variables.
+ (with { a = 1; b = 2; };
+ a + b)
+ # => 3
+
+ # Inner bindings shadow outer bindings.
+ (with { a = 1; b = 2; };
+ (with { a = 5; };
+ a + b))
+ #=> 7
+
+ # This first line of tutorial starts with "with builtins;"
+ # because builtins is a set the contains all of the built-in
+ # functions (length, head, tail, filter, etc.). This saves
+ # us from having to write, for example, "builtins.length"
+ # instead of just "length".
+
+
+ # Set patterns
+ #=========================================
+
+ # Sets are useful when we need to pass multiple values
+ # to a function.
+ (args: args.x + "-" + args.y) { x = "a"; y = "b"; }
+ #=> "a-b"
+
+ # This can be written more clearly using set patterns.
+ ({x, y}: x + "-" + y) { x = "a"; y = "b"; }
+ #=> "a-b"
+
+ # By default, the pattern fails on sets containing extra keys.
+ ({x, y}: x + "-" + y) { x = "a"; y = "b"; z = "c"; }
+ #=> error: anonymous function called with unexpected argument ‘z’
+
+ # Adding ", ..." allows ignoring extra keys.
+ ({x, y, ...}: x + "-" + y) { x = "a"; y = "b"; z = "c"; }
+ #=> "a-b"
+
+
+ # Errors
+ #=========================================
+
+ # `throw` causes evaluation to abort with an error message.
+ (2 + (throw "foo"))
+ #=> error: foo
+
+ # `tryEval` catches thrown errors.
+ (tryEval 42)
+ #=> { success = true; value = 42; }
+ (tryEval (2 + (throw "foo")))
+ #=> { success = false; value = false; }
+
+ # `abort` is like throw, but it's fatal; it cannot be caught.
+ (tryEval (abort "foo"))
+ #=> error: evaluation aborted with the following error message: ‘foo’
+
+ # `assert` evaluates to the given value if true;
+ # otherwise it throws a catchable exception.
+ (assert 1 < 2; 42)
+ #=> 42
+ (assert 1 > 2; 42)
+ #=> error: assertion failed at (string):1:1
+ (tryEval (assert 1 > 2; 42))
+ #=> { success = false; value = false; }
+
+
+ # Impurity
+ #=========================================
+
+ # Because repeatability of builds is critical to the Nix package
+ # manager, in which, functional purity is emphasized in the Nix
+ # language. But there are a few impurities.
+
+ # You can refer to environment variables.
+ (getEnv "HOME")
+ #=> "/home/alice"
+
+ # The trace function is used for debugging. It prints the first
+ # argument to stderr and evaluates to the second argument.
+ (trace 1 2)
+ #=> trace: 1
+ #=> 2
+
+ # You can write files into the Nix store. Although impure, this is
+ # fairly safe because the file name is derived from the hash of
+ # its contents. You can read files from anywhere. In this example,
+ # we write a file into the store, and then read it back out.
+ (let filename = toFile "foo.txt" "hello!"; in
+ [filename (builtins.readFile filename)])
+ #=> [ "/nix/store/ayh05aay2anx135prqp0cy34h891247x-foo.txt" "hello!" ]
+
+ # We can also download files into the Nix store.
+ (fetchurl "https://example.com/package-1.2.3.tgz")
+ #=> "/nix/store/2drvlh8r57f19s9il42zg89rdr33m2rm-package-1.2.3.tgz"
+
+]
+```
+
+### Further Reading
+
+* [Nix Manual - Nix expression language]
+ (https://nixos.org/nix/manual/#ch-expression-language)
+
+* [James Fisher - Nix by example - Part 1: The Nix expression language]
+ (https://medium.com/@MrJamesFisher/nix-by-example-a0063a1a4c55)
+
+* [Susan Potter - Nix Cookbook - Nix By Example]
+ (http://funops.co/nix-cookbook/nix-by-example/)