notes-computer-programming-nock-nockNotes

Cheat sheet

a: state

0: /(b)
1: arg(b)
2: nock(b,c)
3: ?(b)
4: +(b)
5: =(b)
6: if(b,c,d)
7: compose(b,c)
8: assign(b,c)
9: call(b,c)
10a: hint(b,c)
10b: hint(b.c,d)

quotes from Nock documentation (http://urbit.org/doc/)

What one fool can do, another can.

You get used to it. I don't even see the code. All I see is blonde, brunette, redhead. - The Matrix

But are you crazy enough? - Point Break

misc notes

'a' is basically the state, which is being passed around.

copy of spec

from http://www.urbit.org/2013/08/22/Chapter-2-nock.htm :

1  ::    nock(a)           *a
2  ::    [a b c]           [a [b c]]
3  ::  
4  ::    ?[a b]            0
5  ::    ?a                1
6  ::    +[a b]            +[a b]
7  ::    +a                1 + a
8  ::    =[a a]            0
9  ::    =[a b]            1
10 ::
11 ::    /[1 a]            a
12 ::    /[2 a b]          a
13 ::    /[3 a b]          b
14 ::    /[(a + a) b]      /[2 /[a b]]
15 ::    /[(a + a + 1) b]  /[3 /[a b]]
16 ::
17 ::    *[a [b c] d]      [*[a b c] *[a d]]
18 ::
19 ::    *[a 0 b]          /[b a]
20 ::    *[a 1 b]          b
21 ::    *[a 2 b c]        *[*[a b] *[a c]]
22 ::    *[a 3 b]          ?*[a b]
23 ::    *[a 4 b]          +*[a b]
24 ::    *[a 5 b]          =*[a b]
25 ::
26 ::    *[a 6 b c d]      *[a 2 [0 1] 2 [1 c d] [1 0] 2 [1 2 3] [1 0] 4 4 b]
27 ::    *[a 7 b c]        *[a 2 b 1 c]
28 ::    *[a 8 b c]        *[a 7 [[7 [0 1] b] 0 1] c]
29 ::    *[a 9 b c]        *[a 7 c [2 [0 1] [0 b]]]
30 ::    *[a 10 [b c] d]   *[a 8 c 7 [0 3] d]
31 ::    *[a 10 b c]]      *[a c]
32 ::
33 ::    =a                =a
34 ::    /a                /a
35 ::    *a                *a

derivation of rule 6 and commentary

derivation of rule 6, 'if' (line 26)

*[a 2 [0 1] 2 [1 c d] [1 0] 2 [1 2 3] [1 0] 4 4 b]
*[*[a [0 1]] *[a 2 [1 c d] [1 0] 2 [1 2 3] [1 0] 4 4 b]]
*[*[a 0 1] *[a 2 [1 c d] [1 0] 2 [1 2 3] [1 0] 4 4 b]]
*[/[1 a] *[a 2 [1 c d] [1 0] 2 [1 2 3] [1 0] 4 4 b]]
*[a *[a 2 [1 c d] [1 0] 2 [1 2 3] [1 0] 4 4 b]]
*[a *[*[a [1 c d]] *[a [1 0] 2 [1 2 3] [1 0] 4 4 b]]]
*[a *[*[a 1 [c d]] *[a [1 0] 2 [1 2 3] [1 0] 4 4 b]]]
*[a *[[c d] *[a [1 0] 2 [1 2 3] [1 0] 4 4 b]]]
*[a *[[c d] [*[a [1 0]] *[a 2 [1 2 3] [1 0] 4 4 b]]]]
*[a *[[c d] [*[a 1 0] *[a 2 [1 2 3] [1 0] 4 4 b]]]]
*[a *[[c d] [0 *[a 2 [1 2 3] [1 0] 4 4 b]]]]
*[a *[[c d] [0 *[*[a [1 2 3]] *[a [1 0] 4 4 b]]]]]
*[a *[[c d] [0 *[*[a 1 [2 3]] *[a [1 0] 4 4 b]]]]]
*[a *[[c d] [0 *[[2 3] *[a [1 0] 4 4 b]]]]]
*[a *[[c d] [0 *[[2 3] [*[a [1 0]] *[a 4 4 b]]]]]]
*[a *[[c d] [0 *[[2 3] [0 *[a 4 4 b]]]]]]
*[a *[[c d] [0 *[[2 3] [0 +*[a 4 b]]]]]]
*[a *[[c d] [0 *[[2 3] [0 ++*[a b]]]]]]
*[a *[[c d] [0 /[(++*[a b]) [2 3]]]]]
*[a /[/[(++*[a b]) [2 3]] [c d]]]

so if *[a b] is 0, we get:

*[a /[/[(++0) [2 3]] [c d]]]
*[a /[/[2 [2 3]] [c d]]]
*[a /[2 [c d]]]
*[a c]

and if *[a b] is 1, we get:

*[a /[/[(++1) [2 3]] [c d]]]
*[a /[/[3 [2 3]] [c d]]]
*[a /[3 [c d]]]
*[a d]

notes on how this derivation works:

the general form of operations is:

[state operation argument]

state holds all the state (the environment). operation (i'll say 'op') is the current instruction. argument is an argument that will be used by operation.

things are right associative, so if you see [a b c d], that's the same as [a b [c d]], and vice versa.

as written, in lines 19-31 some ops have multiple arguments; but this is the same as taking one argument, due to right associativity.

if you get a list of operations where an operation should be, this means that that list is both an operation and its argument, and that everything after the list is also another operation plus argument, i.e. line 17. This is how you give instructions on how to compute both elements of a pair, e.g. pair generation.

op 2 is used when you want to specify one operation to compute the state, and then do something else on that state. a common special case is when you want to dynamically compute an operation to perform on the state, without changing the state (yet). in this case you must fool with 0 and 1 to produce an identify function on the state. op 2 is the only operation that takes a single * and gives you a *, and also one or more *s on the arguments to the first *.

[a 0 1] is the identity function on state; it simplifies to a.

so, *[a 2 [0 1] x] simplifies to *[a *[a x]], e.g. dynamic computation of the operation to be performed, passing in the current state unchanged.

so by op 2 and op 1, *[a 2 [1 x] y] --> *[x *[a y]], e.g. dynamic computation of the operation to be performed, replacing the state with x

composing pair generation with *[a 1 x], we get something similar to *[a 2 [1 x] y] --> *[x *[a y]], except here we don't want the outside star: *[a [1 x] y] --> [x *[a y]]

applying those idioms, we get:

*[a 2 [0 1] 2 [1 c d] [1 0] 2 [1 2 3] [1 0] 4 4 b]
*[a *[a 2 [1 c d] [1 0] 2 [1 2 3] [1 0] 4 4 b]]      by *[a 2 [0 1] x] --> *[a *[a x]]
*[a *[[c d] *[a [1 0] 2 [1 2 3] [1 0] 4 4 b]]]       by *[a 2 [1 x] y] --> *[x *[a y]]
*[a *[[c d] [0 *[a 2 [1 2 3] [1 0] 4 4 b]]]]         by *[a [1 x] y] --> [x *[a y]]
*[a *[[c d] [0 *[[2 3] *[a [1 0] 4 4 b]]]]]          by *[a 2 [1 x] y] --> *[x *[a y]]
*[a *[[c d] [0 *[[2 3] [0 *[a 4 4 b]]]]]]            by *[a [1 x] y] --> [x *[a y]]

now 0s are selects, and 4s mean +s, so applying those we get:

*[a *[[c d] [0 /[++*[a b] [2 3]]]]]]       
*[a /[/[(++*[a b]) [2 3]] [c d]]] 

note that this is of the form *[a x]. So really this is just a way of dynamically computing the operation and argument. Which operation-and-argument are we computing? x =

/[/[(++*[a b]) [2 3]] [c d]]

so, after applying the idioms, this is where we have to start actually thinking about what is happening.

strangely, if *[a b] is really either 0 or 1, then this expression is equal to

/[(++*[a b]) [c d]]

so the entire thing might have been just

*[a /[(++*[a b]) [c d]]]
*[a [[[c d] 0 (++*[a b])]]]             by removal of / operator
*[a [[[c d] 0 *[a 4 4 b]]]]             by removal of + operator
*[a [[[c d] [0 *[a 4 4 b]]]]]           by associativity
*[a [[[c d] *[a [1 0] 4 4 b]]]]         by *[a [1 x] y] <-- [x *[a y]] (we're trying to bubble up the star)
*[a [c d] *[a [1 0] 4 4 b]]             by associativity
*[a 2 [1 [c d]] [1 0] 4 4 b]            by *[a 2 [1 x] y] <-- *[x *[a y]] (we're trying to bubble up the star)

todo check? yes i think its a little wrong, he gets " *[a 2 [0 1] 2 [1 c d] [1 0] [4 4 b]]" at one point (mb i made a small mistake). , todo

why didn't he use this? he only hints in the doc that it's in case *[a b] is neither 0 nor 1. In this case, i'm not sure but i think the selection operator will depend on evenness/oddness to choose between [2 3] in his original example, and then select with that (e.g. either fst or snd), but with this simplified form, an *[a b] above 1 could cause the branching to be into the tree beneath [c d]. which i'm not sure would be a bad thing... but he doesn't like it i guess.

however afaict the noch 5K spec doesn't define the operation / with an invalid address for the noun it's operating on (because it doesn't define what happens when you try to / into an atom), so i don't see how this helps. mb i am missing something. later: yeah it's to cause a crash, rather than random undefined behavior, if you give bad arguments to 6.

also how do you loop? he explains in his decrement example in the doc. basically you put the program into the state. in the doc, he calls such a state a 'core'. and the program in the state a 'battery.

the benefit of saying *[a 2 [1 [c d]] [1 0] 4 4 b] instead of just saying /[(++*[a b]) [c d]] is:

in the pseudocode in the nock spec in http://www.urbit.org/2013/08/22/Chapter-2-nock.html , the characters *, /, +, = are all used, but actually the 'real' nock instruction set only uses the [] constructor and the natural numbers. So, +s must be converted to op 4s, and /s to op 0s. In addition, the *s must be 'bubbled up' until there is only one * at the top level.

expansions of the macro instructions

6:
 *[a 6 b c d] -->   *[a 2 [0 1] 2 [1 c d] [1 0] 2 [1 2 3] [1 0] 4 4 b]
              -->   *[a /[/[(++*[a b]) [2 3]] [c d]]]

if *[a b] == 0 then *[a c] else *[a d]

7:
 *[a 7 b c]  -->   *[a 2 b 1 c]
             -->   *[*[a b] c]

composition of b and c (first apply b to a, then apply c to the result)

Also, since here the result of *[a b] ends up in the 'state' position for the next step, we might say "mutate a with be, then apply c to the result". Although part of the beauty of Nock is that there is really no 'mutation' and the idea that 'a' is a 'state' is just an interpretation or metaphor.

8:
 *[a 8 b c]  -->   *[a 7 [[7 [0 1] b] 0 1] c]
             -->   *[[*[a b] a] c]

first apply b to a, then cons it with a, producing [*[a b] a], then apply c to this ("push intermediate result onto the stack, then execute c")

note: i say "apply b to a", as if the state were an argument to a function, but another way to say the same thing would be "substitute a into b".

9:
 *[a 9 b c]  -->   *[a 7 c 2 [0 1] 0 b]
             -->   *[*[a c] /[b *[a c]]]

apply c. Select b from this, and execute that.

10:
 *[a 10 [b c] d]  --> *[a 8 c 7 [0 3] d]
 *[a 10 b c] --> *[a c]

Links

Here are a bunch of related links but i'm not recommending any of them in particular:

And less related: