Skip to content

Instantly share code, notes, and snippets.

@deeplook
Created February 21, 2016 14:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save deeplook/965d20f8204f2d8843ef to your computer and use it in GitHub Desktop.
Save deeplook/965d20f8204f2d8843ef to your computer and use it in GitHub Desktop.
Taking the "Nix tour" with a Python background.

Nix vs. Python

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

1. A tour of Nix

Welcome to 'A tour of Nix', a beautifully crafted introduction into the Nix programming language. [...]

2. How it works...

Nix:

{
  v="understood";
}

Python:

v = 'understood'

3. Hello World

Nix:

let 
  h = "Hello";
  w = "World";
in
{
  helloWorld = h + " " + w;
}

Python:

h = "Hello"
w = "World"
helloWorld = h + " " + w

4. Strings

Nix:

let 
  h = "Hello";
in
{
  helloWorld = "${h} World";
}

Python:

h = "Hello"
helloWorld = "{h} World".format(h=h)

5. Integer to string

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))

6. Functions: Introduction

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")

7. Functions with 'attribute sets'

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)

8. Functions: your part...

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)

9. Functions: default values

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

10. Functions: the @-pattern

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)

11. Functions: the @-pattern II

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="")

12. Partial application (???)

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

13. Attribute sets: rec

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

14. Attribute sets: examples 1

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"}

15. Attribute sets: examples 2

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}

16. Attribute sets: examples 3

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:

...

17. Attribute sets: merging

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" ###

18. Attribute sets and booleans

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"]

19. Lists

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"];

20, 21, 22

TBC...

23. Assertions

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)

24, 25

TBC...

26. Fold: Introduction

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

27, 28, 29, 30, 31

TBC...

32. The End

Now hopefully we've learned something about the differences between Nix and Python...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment