Skip to content

Commit 54d39a8

Browse files
committed
code for the fourth article
1 parent 6ece4c9 commit 54d39a8

31 files changed

+701
-3
lines changed

README.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,16 @@ This git repository contains code presented in a series of articles
33
introducing functional programming via Nix's language:
44

55
1. [On Nix's Language: Introduction][tales-nix-1];
6-
2. [On Nix's Language: Recursive Functions][tales-nix-2].
7-
2. [On Nix's Language: Closures][tales-nix-3].
6+
2. [On Nix's Language: Recursive Functions][tales-nix-2];
7+
3. [On Nix's Language: Closures][tales-nix-3];
8+
4. [On Nix's Language: Pragmatism, laziness][tales-nix-4].
89

9-
**<u>Note</u>**: Some of the code doesn't compile, on purpose (e.g. [hw1][gh-mb-nix-hw1])
10+
**<u>Note</u>**: Some of the code doesn't compile, on purpose (e.g.
11+
[hw1][gh-mb-nix-hw1]).
1012

1113
[gh-mb-nix-hw1]: https://github.com/mbivert/nix-series-code/blob/master/hw/hw1.nix
1214

1315
[tales-nix-1]: https://tales.mbivert.com/on-nix-language/
1416
[tales-nix-2]: https://tales.mbivert.com/on-nix-language-recursive-functions/
1517
[tales-nix-3]: https://tales.mbivert.com/on-nix-language-closures/
18+
[tales-nix-4]: https://tales.mbivert.com/on-nix-language-pragmatism-laziness/

lazy/list.nix

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#!/bin/nix-instantiate
2+
with builtins;
3+
let
4+
nil = null;
5+
cons = h: t: [h t];
6+
car = xs: elemAt xs 0;
7+
cdr = xs: elemAt xs 1;
8+
9+
# And that's exactly the code we have for the
10+
# previous version: just the list implementation
11+
# has changed.
12+
zeroes = cons 0 (zeroes);
13+
take = n: xs: let aux = acc: xs: i:
14+
if i == n then
15+
acc
16+
else
17+
aux (cons (car xs) acc) (cdr xs) (i + 1)
18+
; in aux nil xs 0;
19+
xs = take 5 zeroes;
20+
in
21+
deepSeq xs xs

lazy/lists.nix

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#!/bin/nix-instantiate
2+
with builtins;
3+
let
4+
nil = null;
5+
isEmpty = xs: xs == nil;
6+
cons = x: xs: {
7+
car = x;
8+
cdr = xs;
9+
};
10+
car = xs: xs.car;
11+
cdr = xs: xs.cdr;
12+
13+
zeroes = cons 0 (zeroes);
14+
15+
# Actually we would need to reverse the output, or to use
16+
# append instead of cons (more expensive), but it's good
17+
# enough to demonstrate our point.
18+
take = n: xs: let aux = acc: xs: i:
19+
if i == n then
20+
acc
21+
else
22+
aux (cons (car xs) acc) (cdr xs) (i + 1)
23+
; in aux nil xs 0;
24+
xs = take 5 zeroes;
25+
in
26+
deepSeq xs xs
27+

lazy/set.nix

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#!/bin/nix-instantiate
2+
with builtins;
3+
let
4+
nil = null;
5+
isEmpty = xs: xs == nil;
6+
cons = x: xs: {
7+
car = x;
8+
cdr = xs;
9+
};
10+
car = xs: xs.car;
11+
cdr = xs: xs.cdr;
12+
13+
zeroes = cons 0 (zeroes);
14+
15+
# Actually we would need to reverse the output, or to use
16+
# append instead of cons (more expensive), but it's good
17+
# enough to demonstrate our point.
18+
take = n: xs: let aux = acc: xs: i:
19+
if i == n then
20+
acc
21+
else
22+
aux (cons (car xs) acc) (cdr xs) (i + 1)
23+
; in aux nil xs 0;
24+
xs = take 5 zeroes;
25+
in
26+
deepSeq xs xs
27+

lazy/sieve.nix

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#!/bin/nix-instantiate
2+
with builtins;
3+
let
4+
# Any lazy list implementation would do, as
5+
# long as it respect our "API".
6+
nil = null;
7+
isEmpty = xs: xs == nil;
8+
cons = h: t: [h t];
9+
car = xs: elemAt xs 0;
10+
cdr = xs: elemAt xs 1;
11+
12+
take = n: xs: let aux = acc: xs: i:
13+
if i == n then
14+
acc
15+
else
16+
aux (cons (car xs) acc) (cdr xs) (i + 1)
17+
; in aux nil xs 0;
18+
19+
reverse = xs: let aux = acc: xs:
20+
if isEmpty xs then acc
21+
else aux (cons (car xs) acc) (cdr xs)
22+
; in aux nil xs
23+
;
24+
25+
toFloat = n: n + 0.1 - 0.1;
26+
isDiv = i: j: ceil(toFloat(i) / j) * j == i;
27+
28+
sieve = let aux = p: i:
29+
if i != 1 && p i then
30+
# This is the most "correct" variant for the accumulated
31+
# closure; but both would work OK in this context.
32+
cons i (aux (j : (j <= i || !(isDiv j i)) && (p j)) (i + 1))
33+
else
34+
aux p (i + 1)
35+
; in aux (i: i != 1) 1;
36+
37+
xs = reverse (take 10 sieve);
38+
in
39+
deepSeq xs xs
40+

list/iterate.nix

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#!/bin/nix-instantiate
2+
with builtins;
3+
let
4+
iterate = f: n: [f n]++(iterate f (f n));
5+
take = n: xs:
6+
if n == 0 then
7+
[]
8+
else
9+
[(head xs)]++(take (n - 1) (tail xs))
10+
;
11+
xs = take 5 (iterate (x: 2*x) 1);
12+
in
13+
deepSeq xs xs

list/list0.nix

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#!/bin/nix-instantiate
2+
["hello" "world"]

list/list1.nix

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#!/bin/nix-instantiate
2+
[("hel" + "lo") "world"]

list/list2.nix

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#!/bin/nix-instantiate
2+
[1 ("hello") (x: 3)]

list/list3.nix

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#!/bin/nix-instantiate
2+
[(1+3) "hi" ("hello"+" "+"world") 3 (x: 3)]

list/list4.nix

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#!/bin/nix-instantiate
2+
let
3+
# toString can't coerce a function (e.g. (x: 3)) to a
4+
# string, hence why we're applying it here
5+
xs = [(1+3) "hi" ("hello"+" "+"world") 3 ((x: 3) 2)];
6+
in
7+
"${toString xs}"

list/list5.nix

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#!/bin/nix-instantiate
2+
with builtins;
3+
let
4+
xs = []++[1 2]++[]++[3 4]++[5];
5+
in
6+
"${toString xs}"

list/list6.nix

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#!/bin/nix-instantiate
2+
with builtins;
3+
let
4+
xs = [(1+3) "hi" ("hello"+" "+"world") 3 (x: 3)];
5+
in
6+
deepSeq xs xs

list/list7.nix

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#!/bin/nix-instantiate
2+
with builtins;
3+
let
4+
# concatLists' parameter *must* be a list of lists, no
5+
# scalars allowed down there
6+
xs = concatLists (map (x: if !isList x then [x] else x) [[1 2] 3]);
7+
8+
# This is a more efficient and more general "variant" of
9+
# concatLists (equivalent to concatLists (map f xs))
10+
ys = concatMap (x: if !isList x then [x] else x) [[1 2] 3 [4]];
11+
12+
# the ' indicates that foldl is *strict* here, meaning, it
13+
# will systematically evaluate the list entries before processing
14+
# them (like deepSeq does).
15+
n = foldl' add 0 (map (x: 2*x) [1 2 3]);
16+
in
17+
trace(xs)
18+
trace(ys)
19+
trace(n)
20+
"ok"
21+

list/list8.nix

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#!/bin/nix-instantiate
2+
with builtins;
3+
let
4+
map = f: xs: let n = length xs; aux = acc: i:
5+
if i >= n then
6+
acc
7+
else
8+
aux (acc++[(f (elemAt xs i))]) (i + 1)
9+
; in aux [] 0;
10+
11+
map2 = f: xs: if xs == [] then xs
12+
else [(f (head xs))] ++ (map2 f (tail xs));
13+
14+
xs = map (x: 2*x) [1 2 3];
15+
ys = map2 (y: 2*y) [1 2 3];
16+
in
17+
trace(deepSeq xs xs)
18+
trace(deepSeq ys ys)
19+
"ok"

list/take.nix

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#!/bin/nix-instantiate
2+
with builtins;
3+
let
4+
# with head/tail
5+
take = n: xs:
6+
if xs == [] || n == 0 then
7+
[]
8+
else
9+
[(head xs)]++(take (n - 1) (tail xs))
10+
;
11+
12+
# with elemAt and an auxiliary function
13+
take2 = n: xs: let m = length xs; aux = acc: i:
14+
if i >= m then
15+
acc
16+
else
17+
aux (acc++[(elemAt xs i)]) (i + 1)
18+
; in aux [] 0;
19+
20+
# and with foldl'
21+
take3 = n: xs: elemAt (foldl' (acc: x:
22+
let
23+
m = elemAt acc 0;
24+
ys = elemAt acc 1;
25+
in
26+
if m >= n then acc
27+
else [(m + 1) (ys++[x])]
28+
) [0 []] xs) 1;
29+
30+
xs = take 5 [1 2];
31+
ys = take2 2 [1 2 3 4 5];
32+
zs = take 0 [];
33+
ts = take3 3 [1 2 3 4 5];
34+
us = take3 9 [1 2 3 4 5];
35+
in
36+
trace(deepSeq xs xs)
37+
trace(deepSeq ys ys)
38+
trace(deepSeq zs zs)
39+
trace(deepSeq ts ts)
40+
trace(deepSeq us us)
41+
"ok"

list/zipper.nix

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#!/bin/nix-instantiate
2+
with builtins;
3+
let
4+
cons = h: t: [h t];
5+
car = xs: elemAt xs 0;
6+
cdr = xs: elemAt xs 1;
7+
8+
zeroes = cons 0 (zeroes);
9+
10+
tape = b: e: a: [b e a];
11+
prev = xs: elemAt xs 0;
12+
elem = xs: elemAt xs 1;
13+
next = xs: elemAt xs 2;
14+
15+
right = t: tape (cons (elem t) (prev t)) (car (next t)) (cdr (next t));
16+
left = t: tape (cdr (prev t)) (car (prev t)) (cons (elem t) (next t));
17+
18+
# Obviously, there are other ways of altering the current cell,
19+
# this is good enough for us
20+
alter = t: n: tape (prev t) ((elem t) + n) (next t);
21+
22+
start = tape zeroes 0 zeroes;
23+
mem = alter (left (left (alter (right (alter start 5)) 3))) 1;
24+
in
25+
trace(deepSeq mem mem)
26+
"ok"

match.nix

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
#!/bin/nix-instantiate
2+
with builtins;
3+
with (import ./string/strings.nix);
4+
let
5+
matchstar = y: xs: re: let x = charAt xs 0; in
6+
if x == "" then
7+
false
8+
else if matchhere xs re then
9+
true
10+
else if x == y || y == "." then
11+
matchstar y (eat1 xs) re
12+
else
13+
false;
14+
15+
matchhere = xs: re:
16+
let
17+
x = charAt xs 0;
18+
r = charAt re 0;
19+
s = charAt re 1;
20+
in
21+
if r == "" then
22+
true
23+
else if s == "*" then
24+
matchstar r xs (eat1 (eat1 re))
25+
else if r == "$" && s == "" then
26+
x == ""
27+
else if x != "" && (r == "." || r == x) then
28+
matchhere (eat1 xs) (eat1 re)
29+
else
30+
false
31+
;
32+
33+
match1 = xs: re:
34+
let
35+
# Note that charAt returns "" when there's not enough bytes
36+
x = charAt xs 0;
37+
r = charAt re 0;
38+
in
39+
if x == "" && r != "" then false
40+
else if matchhere xs re then true
41+
else match1 (eat1 xs) re
42+
;
43+
44+
match = xs: re: let r = charAt re 0; in
45+
if r == "^" then matchhere xs (eat1 re)
46+
else match1 xs re;
47+
48+
xmatch = s: re: if match s re then "true" else "false";
49+
in
50+
trace(''match("", ""): ${xmatch "" ""}'')
51+
trace(''match("foobar", ""): ${xmatch "foobar" ""}'')
52+
trace(''match("foobar", "foo"): ${xmatch "foobar" "foo"}'')
53+
trace(''match("foobar", "bar"): ${xmatch "foobar" "bar"}'')
54+
trace(''match("foobar", "baz"): ${xmatch "foobar" "baz"}'')
55+
trace(''match("barbaz", "baz"): ${xmatch "barbaz" "baz"}'')
56+
trace(''match("barbaz", ".ar"): ${xmatch "barbaz" ".ar"}'')
57+
trace(''match("barbaz", "ar."): ${xmatch "barbaz" "ar."}'')
58+
trace(''match("aaa", "b*"): ${xmatch "aaa" "b*"}'')
59+
trace(''match("aaa", "b.*"): ${xmatch "aaa" "b.*"}'')
60+
trace(''match("foobar", "b.*"): ${xmatch "foobar" "b.*"}'')
61+
trace(''match("foobar", "o.*a"): ${xmatch "foobar" "o.*a"}'')
62+
trace(''match("foobar", "^foo"): ${xmatch "foobar" "^foo"}'')
63+
trace(''match("foobar", "^bar"): ${xmatch "foobar" "^bar"}'')
64+
trace(''match("foobar", "oob$"): ${xmatch "foobar" "oob$"}'')
65+
trace(''match("foobar", "bar$"): ${xmatch "foobar" "bar$"}'')
66+
trace(''match("foobar", "o.*z*"): ${xmatch "foobar" "o.*z*"}'')
67+
trace(''match("foobar", "o.*z.*"): ${xmatch "foobar" "o.*z.*"}'')
68+
trace(''match("fo^bar", "o^b"): ${xmatch "fo^bar" "o^b"}'')
69+
trace(''match("foobar", "o^b"): ${xmatch "foobar" "o^b"}'')
70+
trace(''match("foobar", "o$b"): ${xmatch "foobar" "o$b"}'')
71+
"ok"

set/mod-fib.nix

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Note that rec is mandatory here
2+
rec {
3+
fib = n:
4+
if n == 0 || n == 1 then
5+
n
6+
else
7+
fib(n - 1)+fib(n - 2)
8+
;
9+
}

set/mod.nix

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#!/bin/nix-instantiate
2+
with builtins;
3+
let
4+
m = (import ./mod-fib.nix);
5+
f = (import ./mod-fib.nix).fib;
6+
xs = [ (m.fib 10) (f 10) ];
7+
in
8+
deepSeq xs xs

0 commit comments

Comments
 (0)