5aliases = ["docs/learn/nock/example"]
7Some people write assembly language by hand. Some of them have a
8good reason to do so. But there's no good reason to write Nock by
11Except for the purposes of learning Nock. It's not necessary to
12know Nock to program in Hoon. But it's quite desirable. The
13feeling of understanding the system at two levels is pleasant.
17A good practice exercise for Nock is a decrement formula. Ie, a
18formula `f` which implements the partial function that produces
19`(s - 1)` if `s` is a nonzero atom, and otherwise does not
22As we know, the equivalent formula for increment is
31~>zod:dojo .*(42 [4 0 1])
35Of course, increment is built into Nock. So, ha, that's easy.
37The best way to learn Nock is to **stop reading right now**, and
38go write your own decrement formula. There's no substitute for
39doing it yourself. But the second best way is faster...
43How do we decrement? A good way to start is to gaze fondly on
44how we'd do it if we actually had a real language, ie, Hoon.
45Here is a minimal decrement in Hoon:
56Or for fun, on one line:
59=>(a=. =+(b=0 |-(?:(=(a +(b)) b $(b +(b))))))
62Does Hoon actually work?
65~zod:dojo> =>(42 =>(a=. =+(b=0 |-(?:(=(a +(b)) b $(b +(b)))))))
69Let's translate this into English. How do we decrement the
70subject? First (line 1), we rename the subject `a`. Second
71(line 2), we add a variable, `b`, an atom with value `0`.
72Third (line 3), we loop. Fourth, we test if `a` equals `b` plus
731 (line 4), produce `b` if it does (line 5), repeat the loop with
74`b` set to `b` plus 1 (line 6) if it doesn't. Obviously, while
75the syntax is unusual, the algorithm is anything but deep. We
76are calculating `b` minus one by counting up from `0`.
78(Obviously, this is an O(n) algorithm. Is there a better way?
79There is not. Do we actually do this in practice? Yes and no.)
81Unfortunately we are missing a third of our Rosetta stone. We
82have decrement in Hoon and we have it in English. How do we
83express this in Nock? What will the Hoon compiler generate from
84the code above? Let's work through it line by line.
86Nock has no types, variable names, etc. So line 1 is a no-op.
88How do we add a variable (line 2)? We compute a new subject,
89which is a cell of the present subject and the variable. With
90this new subject, we execute another formula.
92Since `0` is a constant, a formula that produces it is
98To combine `0` with the subject, we compute
104which, if our subject is 42, gives us
110which we can use as the subject for an inner formula, `g`.
111Composing our new variable with `g`, we have `f` as
114[2 [[1 0] [0 1]] [1 g]]
117which seems a little funky for something so simple. But we
118can simplify it with the composition macro, `7`:
124and still further with the augmentation macro, `8`:
130If you refer back to the Nock definition, you'll see that all
131these formulas are semantically equivalent.
133Let's continue with our decrement. So what's `g`? We seem to
134loop. Does Nock have a loop instruction? It most certainly does
135not. So what do we do?
137We build a noun called a **core** - a construct which is behind any
138kind of interesting control flow in Hoon. Of course, the Nock
139programmer is not constrained to use the same techniques as the
140Hoon compiler, but it is probably a good idea.
142In Hoon, all the flow structures from your old life as an Earth
143programmer become cores. Functions and/or closures are cores,
144objects are cores, modules are cores, even loops are cores.
146The core is just a cell whose tail is data (possibly containing
147other cores) and whose head is code (containing one or more
148formulas). The tail is the **payload** and the head is the
149**battery**. Hence your core is
155To activate a core, pick a formula out of the battery, and use
156the entire core (**not** just the payload) as the subject.
158(A core formula is called an **arm**. An arm is almost like an
159object-oriented method, but not quite - a method would be an arm
160that produces a function on an argument. The arm is just a
161function of the core, ie, a computed attribute.)
163Of course, because we feed it the entire core, our arm can
164invoke itself (or any other formula in the battery). Hence, it
165can loop. And this is what a loop is - the simplest of cores.
167We need to do two things with this core: create it, and activate
168it. To be precise, we need two formulas: a formula which
169produces the core, and one which activates its subject. We can
170compose these functions with the handy `7` instruction:
176`p` produces our core, `a` activates it. Let's take these in
177reverse order. How do we activate a core?
179Since we have only one formula, it's the battery itself.
180Thus we want to execute Nock with the whole core (already the
181subject, and the entire battery (slot `2`)). Hence, `a` is
187We could also use the handy `9` macro - which almost seems
188designed for firing arms on cores:
194Which leaves us seeking
197[8 [1 0] [7 p [9 2 0 1]]]
200And all we have to do is build the core, `p`. How do we build a
201core? We add code to the subject, just as we added a variable
202above. The initial value of our counter was a constant, `0`.
203The initial (and permanent) value of our battery is a constant,
204the loop formula `l`. So `p` is
210Which would leave us seeking
213[8 [1 0] [7 [8 [1 l] [0 1]] [9 2 0 1]]]
216except that we have duplicated the `8` pattern again, since we
220[7 [8 [1 l] [0 1]] [9 2 0 1]]
229so the full value of `f` is
232[8 [1 0] [8 [1 l] [9 2 0 1]]]
235Thus our only formula to compose is the loop body, `l`.
236Its subject is the loop core:
242where `bat` is just the loop formula, and `pay` is the pair `[b
243a]`, `b` being the counter, and `a` the input subject. Thus we
244could also write this subject as
250and we see readily that `a` is at slot `7`, `b` `6`, `l` `2`.
251With this subject, we need to express the Hoon loop body
254?: =(a +(b)) :: line 4
259This is obviously an if statement, and it calls for `6`. Ie:
265Giving our decrement program as:
268[8 [1 0] [8 [1 6 t y n] [9 2 0 1]]]
271For `t`, how do we compute a flag that is yes (`0`) if `a` equals
272`b` plus one? Equals, we recall, is `5`. So `t` can only be
278If so, our product `y` is just the counter `b`:
284And if not? We have to re-execute the loop with the counter
285incremented. If we were executing it with the same counter,
286obviously an infinite loop, we could use the same core:
292But instead we need to construct a new core with the counter
308[9 2 [[0 2] [4 0 6] [0 7]]]
311Hence our complete decrement. Let's reformat vertically so we
340 [9 2 [[0 2] [4 0 6] [0 7]]]
348or, on one line without superfluous brackets:
351[8 [1 0] 8 [1 6 [5 [0 7] 4 0 6] [0 6] 9 2 [0 2] [4 0 6] 0 7] 9 2 0 1]
354which works for the important special case, 42:
357~zod:dojo> .*(42 [8 [1 0] 8 [1 6 [5 [0 7] 4 0 6] [0 6] 9 2 [0 2] [4 0 6] 0 7] 9 2 0 1])
361If you understood this, you understand Nock. At least in principle!
362Now, go write an adder. Or don't.
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list