An attempt at contrasting Nix and Python syntax (maybe helpful to grasp Nix faster for those with a Python background) based on the "Nix tour" found here: https://nixcloud.io/tour/?id=1
Welcome to 'A tour of Nix', a beautifully crafted introduction into the Nix programming language. [...]
Nix:
{
v="understood";
}
Python:
v = 'understood'
Nix:
let
h = "Hello";
w = "World";
in
{
helloWorld = h + " " + w;
}
Python:
h = "Hello"
w = "World"
helloWorld = h + " " + w
Nix:
let
h = "Hello";
in
{
helloWorld = "${h} World";
}
Python:
h = "Hello"
helloWorld = "{h} World".format(h=h)
Nix:
let
h = "Strings";
value = 4;
in
{
helloWorld = "${h} ${toString value} the win!";
}
Python:
h = "Strings"
value = 4
helloWorld = "{h} {value} the win!".format(h=h, value=str(value))
Nix:
let
f = "f";
o = "o";
func = a: b: c: a + b + c;
in
{
foo = func f o "o";
}
Python:
f = "f"
o = "o"
def func(a, b, c):
return a + b + c
foo = func(f, o, "o")
Nix:
let
f = "f";
o = "o";
func = {a, b, c}: a+b+c;
in
{
foo = func {a=f; b=o; c=o;};
}
Python:
f = "f"
o = "o"
def func(a, b, c):
return a + b + c
foo = func(a=f, b=o, c=o)
Nix:
let
min = x: y: if x < y then x else y;
max = x: y: if x > y then x else y;
in
{
ex1 = min 5 3;
ex2 = max 9 4;
}
Python:
min = lambda x, y: x if x < y else y
max = lambda x, y: x if x > y else y
ex1 = min(5, 3)
ex2 = max(9, 4)
Nix:
let
f = "f";
o = "o";
b = "b";
func = {a ? f, b ? "a", c ? ""}: a + b + c; #Only modify this line!
in
rec {
foo = func {b="o"; c=o;}; #should be foo
bar = func {a=b; c="r";}; #should be bar
foobar = func {a=foo;b=bar;}; #should be foobar
}
Python:
f = "f"
o = "o"
b = "b"
def func(a=f, b="a", c=""):
return a + b + c; #Only modify this line!
foo = func(b="o", c=o) #should be foo
bar = func(a=b, c="r") #should be bar
foobar = func(a=foo, b=bar) #should be foobar
Nix:
let
arguments = {a="f"; b="o"; c="o"; d="bar";}; #only modify this line
func = {a, b, c, ...}: a+b+c;
func2 = args@{a, b, c, ...}: a+b+c+args.d;
in
{
#the argument d is not used
foo = func arguments;
#now the argument d is used
foobar = func2 arguments;
}
Python:
arguments = {"a":"f", "b":"o", "c":"o", "d":"bar"} #only modify this line
def func(a, b, c, **args): return a + b + c
def func2(a, b, c, **args): return a + b + c + args["d"]
#the argument d is not used
foo = func(**arguments)
#now the argument d is used
foobar = func2(**arguments)
Nix:
let
func = {a, b, ...}@bargs: if a == "foo" then
b + bargs.c else b + bargs.x + bargs.y;
in
{
#complete next line so it evaluates to "foobar"
foobar = func {a="bar"; b="foo"; x="bar"; y="";};
}
Python:
def func(a, b, **bargs):
if a == "foo":
return b + bargs["c"]
else:
return b + bargs["x"] + bargs["y"]
foobar = func(a="bar", b="foo", x="bar", y="")
Nix:
let
b = 1;
fu0 = (x: x);
fu1 = (x: y: x + y) 4;
fu2 = (x: y: (2 * x) + y);
in
rec {
ex00 = fu0 4; # must return 4
ex01 = (fu1) 1; # must return 5
ex02 = (fu2 2) 3; # must return 7
ex03 = (fu2 3); # must return <LAMBDA>
ex04 = ex03 1; # must return 7
ex05 = (n: x: (fu2 x n)) 3 2; # must return 7
}
Python:
from functools import partial
b = 1
def fu0(x): return x
fu1 = partial(lambda x, y: x+y, 4)
fu2 = lambda x: lambda x: (2 * x) + y
ex00 = fu0(4) # must return 4
ex01 = fu1(1) # must return 5
ex02 = (fu2 2) 3; # must return 7
ex03 = (fu2 3); # must return <LAMBDA>
ex04 = ex03 1; # must return 7
ex05 = (n: x: (fu2 x n)) 3 2; # must return 7
Nix:
rec {
x = "a";
y = x;
}
#Recursive sets are just normal sets, but the attributes can refer to each other.
#Be aware of infinit recursions. They are not possible!
#rec {
# x = y;
# y = x;
#} Does not Work.
Python:
class Record(object):
x = "a"
y = x
Nix:
with import <nixpkgs> { };
with stdenv.lib;
let
attr = {a="a"; b = 1; c = true;};
s = "b";
in
{
#replace all X, everything should evaluate to true
ex0 = isAttrs attr;
ex1 = attr.a == "a";
ex2 = attr.${s} == 1;
ex3 = attrVals ["c" "b"] attr == [true 1];
ex4 = attrValues attr == ["a" 1 true];
ex5 = builtins.intersectAttrs attr {a="b"; d=234; c="";} == { a = "b"; c="";};
ex6 = removeAttrs attr ["b" "c"] == {a = "a";};
}
Python:
attr = {"a": "a", "b": 1, "c": True}
s = "b"
ex0 = type(attr) == dict
ex1 = attr["a"] == "a"
ex2 = attr[s] == 1
ex3 = [attr[k] for k in ["c", "b"]] == [True, 1]
ex4 = attr.values() == ["a", 1, True] ##
attr1 = {"a":"b", "d":234, "c":""}; ex5 = {k: attr1[k] for k in attr1 if k in attr} == {"a": "b", "c": ""}
ex6 = del attr["b"]; del attr["c"] # => attr == {"a": "a"}
Nix:
let
list = [ { name = "foo"; value = 123; }
{ name = "bar"; value = 456; } ];
string = ''{"x": [1, 2, 3], "y": null}'';
in
{
ex1 = builtins.listToAttrs list == {foo = 123; bar = 456;};
ex2 = builtins.fromJSON string == {x = [1 2 3]; y = null;};
}
Python:
import json
list = [ {"name": "foo", "value": 123},
{"name": "bar", "value": 456} ]
string = '''{"x": [1, 2, 3], "y": null}'''
ex1 = {d["name"]: d["value"] for d in list} == {"foo": 123, "bar": 456}
ex2 = json.loads(string) == {"x": [1, 2, 3], "y": None}
Nix:
let
attrSetBonus = {f = {add = (x: y: x + y);
mul = (x: y: x * y);};
n = {one = 1; two = 2;};};
in
rec {
#Bonus: use only the attrSetBonus to solve this one
exBonus = with attrSetBonus; 5 == f.add (f.mul n.two n.two) n.one ;
}
Python:
...
Nix:
with import <nixpkgs> { };
let
x = { a="bananas"; b= "pineapples"; };
y = { a="kakis"; c ="grapes";};
z = { a="raspberrys"; c= "oranges"; };
func = {a, b, c ? "another secret ingredient"}: "A drink of: " +
a + ", " + b + " and " + c;
in
rec {
ex00=func (x);
ex01=func (y // x );
ex02=func (x // { c="lychees";});
ex03=func (z // x // z);
}
Python:
class updict(dict):
"A dict that returns self after an update()."
def update(self, *args, **kwargs):
dict.update(self, *args, **kwargs)
return self
x = updict(a="bananas", b="pineapples")
y = updict(a="kakis", c="grapes")
z = updict(a="raspberrys", c="oranges")
def func(a, b, c="another secret ingredient"):
return "A drink of: " + a + ", " + b + " and " + c
ex00=func(x) == "A drink of: bananas, pineapples and another secret ingredient"
ex01=func(y.update(x)) == A drink of: bananas, pineapples and grapes"
ex02=func(x.update({"c": "lychees"})) == "A drink of: bananas, pineapples and lychees"
ex03=func(z.update(x).update(z)) == "A drink of: raspberrys, pineapples and oranges" ###
Nix:
let
attrSet = {x = "a"; y = "b"; b = {t = true; f = false;};};
attrSet.c = 1;
attrSet.d = null;
attrSet.e.f = "g";
in
rec {
ex0 = attrSet.b.t;
#equal
ex01 = "a" == attrSet.x;
#unequal
ex02 = !("b" != attrSet.y);
#and/or/neg
ex03 = ex01 && !ex02 || !attrSet.b.f;
#implication
ex04 = true -> attrSet.b.t;
#contains attribute
ex05 = attrSet ? e;
ex06 = attrSet.e ? f;
}
Python:
attrSet = {"x": "a", "y": "b", "b": {"t": True, "f": False}}
attrSet["c"] = 1
attrSet["d"] = None
attrSet["e"] = {"f": "g"}
ex0 = attrSet["b"]["t"]
#equal
ex01 = "a" == attrSet["x"]
#unequal
ex02 = not ("b" != attrSet["y"])
#and/or/neg
ex03 = ex01 and not ex02 or not attrSet["b"]["f"]
#implication
ex04 = not True or attrSet["b"]["t"] # e1 -> e2 === !e1 || e2
#contains attribute
ex05 = "e" in attrSet.keys()
ex06 = "f" in attrSet["e"]
Nix:
with import <nixpkgs> { };
with stdenv.lib;
let
list = [2 "4" true true {a = 27;} 2];
f = x: isString x;
s = "foobar";
in
{
#replace all X, everything should evaluate to true
ex00 = isList list;
ex01 = elemAt list 2 == true;
ex02 = length list == 6;
ex03 = last list == 2;
ex04 = filter f list == ["4"];
ex05 = head list == 2;
ex06 = tail list == ["4" true true {a = 27;} 2];
ex07 = remove true list == [2 "4" {a = 27;} 2];
ex08 = toList s == [s];
ex09 = take 3 list == [2 "4" true];
ex10 = drop 4 list == [{a = 27;} 2];
ex11 = unique list == [2 "4" true {a = 27;}];
ex12 = list ++ ["x" "y"] == [2 "4" true true {a = 27;} 2 "x" "y"];
}
Python:
list_ = [2, "4", True, True, {"a": 27}, 2]
f = lambda x: type(x) is str
s = "foobar"
ex00 = type(list_) == list
ex01 = list[2] == True
ex02 = len(list_) == 6
ex03 = list[-1] == 2
ex04 = filter(f, list_) == ["4"]
ex05 = list[0] == 2
ex06 = list[1:] == ["4", True, True, {"a": 27}, 2]
ex07 = filter(lambda x: not x is True, list_) == [2, "4", {"a": 27}, 2]
ex08 = (lambda x: [x])(s) == [s]
ex09 = take 3 list == [2 "4" true];
ex10 = drop 4 list == [{a = 27;} 2];
ex11 = unique list == [2, "4", True, {"a": 27}]
ex12 = list ++ ["x" "y"] == [2 "4" true true {a = 27;} 2 "x" "y"];
TBC...
Nix:
with import <nixpkgs> {};
let
func = x: y: assert (x==2); x + y;
n = -1; # only modify this line
in
assert (lib.isInt n);
rec {
ex00 = func (n+3) 3;
}
Python:
def func(x, y):
assert x == 2
return x + y
n = -1; # only modify this line
assert type(n) == int
ex00 = func(n+3, 3)
TBC...
Nix:
# use fold to write a function that counts all "a" in a list
with import <nixpkgs> { };
let
list = ["a" "b" "a" "c" "d" "a"];
countA = lib.fold (x: y: if x == "a" then 1 + y else y) 0;
in
rec {
example = lib.fold (x: y: x + y) "" ["a" "b" "c"]; #is "abc"
result = countA list; #should be 3
}
Python:
list_ = ["a", "b", "a", "c", "d", "a"]
def countA(some_list):
return reduce(lambda x, y: y+1 if x == "a" else y, some_list, 0)
# works a well:
# return reduce(lambda x, y: x+1 if y == "a" else x, some_list, 0)
countA(list_) == 3
TBC...
Now hopefully we've learned something about the differences between Nix and Python...