on master help sign in
Merge pull request #493 from baudtack/master

workbook update

commitd8f03841205ab2d541e7295598d444abd9035c7e authorrmariani <robert@tlon.io> dateFri 17 May 2019, 10:45 (7 years ago) parentdf1f2a921fd788
5 files changed+348−19
learn/hoon/hoon-tutorial/gates.md+38−5 learn/hoon/hoon-tutorial/nouns.md+3−14 learn/hoon/hoon-tutorial/recursion.udon+200 learn/hoon/workbook/even.md+54 learn/hoon/workbook/fib.md+53
learn/hoon/hoon-tutorial/gates.md+38−5
@@ -50,7 +50,7 @@ Like all arms, `$` is computed with its parent core as the subject. When `$` is
5050Let's make a gate that takes any unsigned integer (i.e., an atom) as its sample and returns that value plus one as the product. To do this we'll use the `|=` rune. We'll bind this gate to the face `inc` for 'increment':
5151
5252```
53−> =inc |=(a=@ (add 1 a))
53+> =inc |=(a=@ +(a))
5454
5555> (inc 1)
56562
@@ -66,13 +66,13 @@ The gate works as promised -- it takes any number `n` and returns `n + 1`. Let'
6666
6767### The `|=` Rune
6868
69−We typically use the `|=` rune to create a gate. In the expression above the `|=` is immediately followed by a set of parentheses containing two subexpressions: `a=@` and `(add 1 a)`. The first defines the gate's sample (input value type), and the second defines the gate's product (value).
69+We typically use the `|=` rune to create a gate. In the expression above the `|=` is immediately followed by a set of parentheses containing two subexpressions: `a=@` and `+(a)`. The first defines the gate's sample (input value type), and the second defines the gate's product (value).
7070
7171When we used the `|%` rune in the last lesson we didn't use parentheses because we were writing in "tall form" syntax, whereas here we are writing in "flat form" syntax. (The latter uses parentheses and is more suitable for shorter snippets of code.) We'll explain these concepts more thoroughly in lesson 2.2.
7272
7373In the example gate above, `inc`, the sample is defined by `a=@`. This means that the sample is defined as an atom, `@`, meaning that the gate will take as input anything of that type. The sample is given the face `a`. With the face it's easier to refer to the sample in later code, if desired.
7474
75−In `inc`, the product is defined by `(add 1 a)`. There's not much to it -- it returns the value of `a + 1`!
75+In `inc`, the product is defined by `+(a)`. There's not much to it -- it returns the value of `a + 1`!
7676
7777The second subexpression after the `|=` rune is used to build the gate's `$` arm. That's where all the computations go.
7878
@@ -251,12 +251,45 @@ We can use `ten(b 25)` to produce a variant of `ten`. Calling this mutant versi
251251175
252252```
253253
254−Before finishing the lesson let's unbind `ten`:
254+Let's unbind `ten`:
255255
256256```
257257> =ten
258258```
259259
260+## Looping
261+
262+Let's examine a new gate.
263+
264+```
265+|= [a=@ud b=@ud]
266+=| c=@ud
267+|-
268+?: =(b 0)
269+ c
270+$(c (add a c), b (dec b))
271+```
272+
273+This gate takes two arguments and multiplies them together using only `add` and `dec` This is an extremely naive way to do multiplication but let's examine it anyway.
274+
275+The first thing worth noticing is the new rune `|-` which you can think of as a looping structure. We're denoting some bit of code that we want to run multiple times. This is a lie but go with it for a moment.
276+
277+The next few lines should be pretty clear, if `b` is `0` we're going to return `c`.
278+When it isn't, we are going to use the irregular form of the `%=` rune, the wide form of this line would look like this.
279+
280+```
281+%=($ c (add a c), b (dec b))
282+```
283+
284+What this rune does is resolve a wing with changes. In this case, and in many you will see, the wing we want to resolve is `$` or the `|-` we are inside. The rest are a set of changes to make to the subject of that wing. We are going to add `a` to `c` and decrement `b` and then run the code again starting from `|-`.
285+
286+In the next lesson, we'll examine `|-` in more detail.
287+
288+### Jets
289+
290+Hoon code is compiled to Nock and Nock is extremely simple and naive in the way a number of things are done. For example, the only mathematical operation Nock has is increment. All other mathematical functions are built out of this. While this is conceptually doable, actually running this code could be unnecessarily slow. This is the reason for jets, a set of functionally identical code that could be run instead of the compiled Nock to speed up execution. To learn more about Jets see [jets doc].
291+
292+
260293## Exercise 1.5.1 Solution
261294
262295Write a gate that takes an atom, `a=@`, and which returns double the value of `a`. Bind this gate to `double` and test it in the dojo.
@@ -271,4 +304,4 @@ Write a gate that takes an atom, `a=@`, and which returns double the value of `a
27130450
272305```
273306
274−### [Next Lesson: Multi-gate Cores and Doors](../multi-gate-cores-and-doors)
307+### [Recursion](../recursion)
learn/hoon/hoon-tutorial/nouns.md+3−14
@@ -24,18 +24,6 @@ Here are some cells:
2424
2525All of the above are nouns.
2626
27−## Recursive Definitions
28−
29−You may or may not have noticed that the definition of 'noun' is [recursive](https://en.wikipedia.org/wiki/Recursion). Recursion is an important concept for functional programming, so it's worth taking a moment to understand it better.
30−
31−A recursive definition has three clauses: a base clause, a generating clause, and a closure clause. Let's say you want to use recursion to define some X. The definition should allow one to determine whether any given object is a member of X. The **base clause** of the recursive definition defines one or more objects as trivial members of X. The **generating clause** defines other objects as also being X by their relationship to previously established members of X. The **closure clause** asserts that nothing else is a member of X than what has been given in the first two clauses. Often the closure clause is not explicitly stated.
32−
33−For example, let's say you want to define the phrase 'ancestor of George Washington'. Which 'objects' are members of that set? The base clause is simple: the parents of George Washington are trivial ancestors of George Washington. The generating clause is: given that some x is an ancestor of George Washington, the parents of x are also ancestors of George Washington. The closure clause is: nothing else is an ancestor of George Washington. That's it!
34−
35−Let's restate the definition of **noun** in these terms. The base clause of the definition of a 'noun' is: all atoms are nouns (where an 'atom' is an unsigned integer). The generating clause is: if x and y are nouns, then the cell `[x y]` is also a noun. (A 'cell' is an ordered pair of nouns.). The closure clause is: nothing else is a noun.
36−
37−We belabor this point about recursion only because it's an important concept for having a firm grasp of functional programming. We'll talk more about recursion at various points in this series.
38−
3927### Nouns as Binary Trees
4028
4129A [binary tree](https://en.wikipedia.org/wiki/Binary_tree) has a single base node, and each node of the tree may have up to two child nodes (but it need not have any). A node without children is a 'leaf'. You can think of a noun as a binary tree whose leaves are atoms, i.e., unsigned integers. All non-leaf nodes are cells. To visualize this, consider the following representation of the noun: `[12 [17 45]]`:
@@ -63,6 +51,7 @@ Each number is an atom; each dot is a cell. Let's look at another example, this
6351
6452An atom is a trivial tree of just one node; e.g., `17`.
6553
54+
6655## Atoms
6756
6857Atoms are unsigned integers. These are often represented by the dojo as unsigned integers in decimal notation, e.g., `17`.
@@ -178,7 +167,7 @@ All of the above are atoms. The underlying noun of each is just an unsigned int
178167313
179168```
180169
181−The `\`@\`` syntax is used to 'cast' a value to a raw atom, i.e., an atom without an aura. Don't worry about exactly what `\`@\`` means just yet. Casts will be explained in more detail in a later lesson. For now all you need to know is that a cast doesn't change the underlying value of the noun; it's used here only to change how the value is printed in the dojo output.
170+The `\`@\`` syntax is used to 'cast' a value to a raw atom, i.e., an atom without an aura. Don't worry about exactly what `\`@\`` means just yet. Casts will be explained in more detail in a later lesson. For now all you need to know is that a cast doesn't change the underlying value of the noun.
182171
183172#### Identities, Cords, and Terms
184173
@@ -314,7 +303,7 @@ In this cell `32` is the head and `320` is the tail. Cells can contain cells, a
314303[[%in 0xa] 'cell']
315304```
316305
317−I said there isn't _much_ mystery about cells, but there is a little. So far whenever we've entered a cell into the dojo, it not only returned the same cell; the cell was printed in the same way it was entered. This doesn't always happen. The dojo is obligated to evaluate the input and return the correct answer, but it doesn't have to show the result in exactly the same way:
306+There isn't _much_ mystery about cells, but there is a little. So far whenever we've entered a cell into the dojo, it not only returned the same cell; the cell was printed in the same way it was entered. This doesn't always happen. The dojo is obligated to evaluate the input and return the correct answer, but it doesn't have to show the result in exactly the same way:
318307
319308```
320309> [6 [62 620]]
learn/hoon/hoon-tutorial/recursion.udonnew file+200
@@ -0,0 +1,200 @@
1+:- :~ navhome/'/docs/'
2+ next/'true'
3+ sort/'3'
4+ title/'Recursion'
5+ ==
6+;>
7+
8+# Recursion
9+
10+Recursion is a common pattern for solving certain problems in most programming
11+languages, and Hoon is no exception. One of the classically recursive problems
12+is that of factorial. The factorial of _n_ is the product of all positive
13+integers less than or equal to N. Thus the factorial of 5, denoted as _5!_,
14+would be:
15+
16+```
17+5 * 4 * 3 * 2 * 1 = 120
18+```
19+
20+### Factorial In Hoon
21+
22+Let's implement a factorial calculator in Hoon. To do so, we'll write a
23+recursive `gate` (Hoon's equivalent of a function) to perform the relevant
24+computation. Save the code below as `factorial.hoon` in your ship's `/gen`
25+directory.
26+
27+```
28+|= n=@ud
29+?: =(n 1)
30+ 1
31+(mul n $(n (dec n)))
32+```
33+
34+Our `gate` takes one sample (argument) `n` that must nest inside `@ud`, the
35+unsigned-integer type.
36+
37+Next we check to see if `n` is `1`. If so, the result is just `1`, since
38+`1 * 1 = 1`.
39+
40+If, however, `n` is _not_ `1`, then we branch to the final line of the code,
41+`(mul n $(n (dec n)))`, where the recursion logic lives. Here, we multiply `n`
42+by the recursion of `n` minus `1`. `$` initiates recursion: it calls the gate
43+that we're already in, but replaces its sample.
44+
45+In our example, we multiply `n` with the product of this _entire gate
46+all over again_ with `$(n (dec n))`, except when that new gate has its sample
47+decremented by one. This works recursively because each new gate will, of
48+course, itself contain the code to call a further-decremented gate. Gates will
49+continue to call new, further-decremented gates until `n` is `1`, and that `1`
50+will be the final number to be multiplied by.
51+
52+Let's run the program in the Dojo:
53+
54+```
55+> +factorial 5
56+120
57+```
58+
59+It may help to visualize the operation of our example gate. The pseudo-Hoon below
60+illustrates what happens when we use it to find the factorial of 5:
61+
62+```
63+(factorial 5)
64+(mul 5 (factorial 4))
65+(mul 5 (mul 4 (factorial 3)))
66+(mul 5 (mul 4 (mul 3 (factorial 2))))
67+(mul 5 (mul 4 (mul 3 (mul 2 (factorial 1)))))
68+(mul 5 (mul 4 (mul 3 (mul 2 1))))
69+(mul 5 (mul 4 (mul 3 2)))
70+(mul 5 (mul 4 6))
71+(mul 5 24)
72+120
73+```
74+
75+It's easy to see how we're "floating" gate calls until we reach the final
76+iteration of such calls that only produces a value. The `mul n` component of the
77+gate leaves something like `mul 5`, waiting for the final series of terms
78+to be operated upon. The `$(n (dec n))` component is what expands out the
79+expression, as illustrated by `(factorial 4)`. Once the expression cannot be
80+expanded out further, the operations work backwards, successively feeding values
81+into the `mul` functions behind them.
82+
83+##### Tail-Call Optimization
84+
85+Our last example isn't a very efficient use computing resources. The
86+pyramid-shaped illustration approximates what's happening on the *call stack*, a
87+memory structure that tracks the instructions of the program. In our example
88+code, every time a parent gate calls another gate, the gate being called is
89+"pushed" to the top of the stack in the form of a frame. This process continues
90+until a value is produced instead of a function, completing the stack.
91+
92+```
93+ Push order Pop order
94+(fifth frame) ^ |
95+(fourth frame) | |
96+(third frame) | |
97+(second frame) | |
98+(first frame) | V
99+```
100+
101+Once this stack of frames is completed, frames "pop" off the stack starting at
102+the top. When a frame is popped, it executes the contained gate and passes
103+produced data to the frame below it. This process continues until the stack
104+is empty, giving us the gate's output.
105+
106+When a program's final expression uses the stack in this way, it's considered to
107+be *not tail-recursive*. This usually happens when the last line of executable
108+code calls more than one gate, our example code's `(mul n $(n (dec n)))` being
109+such a case. That's because such an expression needs to hold each iteration of
110+`$(n (dec n)` in memory so that it can know what to run against the `mul`
111+function every time.
112+
113+To reiterate: if you have to manipulate the result of a recursion as the last
114+expression of your gate, as we did in our example, the function is not
115+tail-recursive, and therefore not very efficient with memory. A problem arises
116+when we try to recurse more times that we have space on the stack. This will
117+result in our computation failing and producing a stack overflow. If we tried
118+to find the factorial of `5.000.000`, for example, we would almost certainly
119+run out of stack space.
120+
121+But the Hoon compiler, like most compilers, is smart enough to notice when the
122+last statement of a parent can reuse the same frame instead of needing to
123+add new ones onto the stack. If we write our code properly, we can use a single
124+frame that simply has its values replaced with each recursion.
125+
126+##### A Tail-Recursive Gate
127+
128+With a bit of refactoring, we can write a version of our factorial gate that
129+_is_ tail-recursive and can take advantage of this feature:
130+
131+```
132+|= n=@ud
133+=/ t=@ud 1
134+|-
135+?: =(n 1)
136+ t
137+$(n (dec n), t (mul t n))
138+```
139+
140+The above code should look familiar. We are still building a gate that
141+takes one argument `n`. This time, however, we are also putting a face on a
142+`@ud` and setting it's initial value to 1. The `|-` here is used to create a new
143+gate with one arm `$` and immediately call it. Think of `|-` as the recursion
144+point.
145+
146+We then evaluate `n` to see if it is 1. If it is we return the value of `t`. In
147+the case that `n` is anything other than 1, we perform our recursion:
148+
149+```
150+$(n (dec n), t (mul t n))
151+```
152+
153+All we are doing here is recursing our new gate and modifying the values of `n`
154+and `t`. `t` is used as an accumulator variable that we use to keep a running
155+total for the factorial computation.
156+
157+Let's use pseudo-Hoon to illustrate how the stack is working in this example for
158+factorial 5.
159+
160+```
161+(factorial 5)
162+(|- 5 1)
163+(|- 4 5)
164+(|- 3 20)
165+(|- 2 60)
166+(|- 1 120)
167+120
168+```
169+
170+We simply multiply `t` and `n` to produce the new value of `t`, and then
171+decrement `n` before repeating. Since this `$` call is the final and solitary
172+thing that is run in the default case and since we are doing all computation
173+before the call, this version is properly tail-recursive. We don't need to do
174+anything to the result of the recursion except recurse it again. That means that
175+each iteration can be replaced instead of held in memory.
176+
177+##### A Note on `$`
178+
179+`$` (pronounced "buc") is, in its use with recursion, a reference to the gate that we are inside
180+of. That's because a gate is just a core with a single arm named `$`. The
181+subject is searched depth-first, head before tail, with faces skipped, and
182+stopping on the first result. In other words, the first match found in the head
183+will be returned. If you wished to refer to the outer `$` in this context, the
184+idiomatic way would be to use [`^$`](/docs/reference/rune/ket). The `^` operator
185+skips the first match of a name.
186+
187+The syntax used is also the irregular form of the `%=` rune.
188+
189+### Exercises
190+
191+1. Write a recursive gate that produces the first _n_
192+[Fibonacci numbers](https://en.wikipedia.org/wiki/Fibonacci_number)
193+
194+2. Write a recursive gate that produces a list of moves to solve the
195+[Tower of Hanoi problem](https://en.wikipedia.org/wiki/Tower_of_Hanoi).
196+Disks are stacked on a pole by decreasing order of size. Move all of the
197+disks from one pole to another with a third pole as a spare, moving one
198+disc at a time, without putting a larger disk on top of a smaller disk.
199+
200+### [Next Lesson: Multi-gate Cores and Doors](../multi-gate-cores-and-doors)
learn/hoon/workbook/even.mdnew file+54
@@ -0,0 +1,54 @@
1+Write a generator that takes an integer and checks if it even and between 1 and 100.
2+
3+```
4+:- %say
5+|= [* [n=@ud ~] ~]
6+:- %noun
7+?: ?& =(0 (mod n 2))
8+ ?& (gte n 1)
9+ ?! (gth n 100)
10+ ==
11+ ==
12+ %.y
13+%.n
14+```
15+
16+On the very first line, with `:- %say` we are beginning to create a generator of the `%say` kind. The result of a `%say` generator is a cell with a head of `%say` and tail that is a gate, itself producing a `cask`, a pair of a `mark` and some data. For more information about `%say` generators, see the [Generators](/using/generators) documentation.
17+
18+```
19+|= [* [n=@ud ~] ~]
20+```
21+
22+The code above builds a gate. The gate's first argument is a cell provided by Dojo that contains some system information we're not going to use, so we use `*` to indicate "any noun." The next cell is our arguments provided to the generator upon invocation at the `dojo`. Here we only want one `@ud` with the face `n`.
23+
24+```
25+:- %noun
26+```
27+
28+This code is the third line of the `%say` "boilerplate, and it produces a `cask` with the head of `%noun`. We could use any `mark` here, but `%noun` is the most generic type, able to fit any data.
29+
30+Below we'll examine the the series of `?` runes used.
31+
32+```
33+?: ?& =(0 (mod n 2))
34+```
35+
36+`?:` is the simplest "wut" rune. It takes three expressions: a boolean test, a yes-branch, and a no-branch. The yes-branch is executed when the test evaluates to `%.y` and the no-branch is executed when the test evaluates to `%.n`. Here the yes-branch is `%.y` and the no-branch is `%.n`. The boolean test is the more complicated portion. First we're going to use `?&` to combine to expressions with a logical and operation.
37+
38+The first expression is:
39+
40+```
41+=(0 (mod n 2))
42+```
43+
44+which simply asks if n is even or not.
45+
46+The second expression is:
47+
48+```
49+ ?& (gte n 1)
50+ ?! (gth n 100)
51+ ==
52+```
53+
54+Again we and two expressions with `?&` the first is just checking if n is greater than or equal to 1. The second we have artificially use the `?!` rune to demonstrate its use. `?!` is a logical NOT operation. We use that on the result of asking if n is greater than 100 to effectively ask if it is less than or equal to 100.
learn/hoon/workbook/fib.mdnew file+53
@@ -0,0 +1,53 @@
1+Write a generator that produce a list of Fibonacci numbers up to n.
2+
3+```
4+|= n=@ud
5+%- flop
6+=+ [i=0 p=0 q=1 r=*(list @ud)]
7+|- ^+ r
8+?: =(i n) r
9+%= $
10+ i +(i)
11+ p q
12+ q (add p q)
13+ r [q r]
14+==
15+```
16+
17+First we need to produce a gate which takes an `@ud` so we know when to stop producing the list.
18+
19+```
20+%- flop
21+```
22+Next we're going to call `flop` on the output of the rest of the program. `flop` is a standard library function to reverse a list. Why are we using this instead of producing the list in the order we want it in the first place? With lists adding an element to the end is an expensive operation that gets more expensive the longer the list is. Adding an element to the front however is cheap. In Big O Notation, adding to the end of a list is O(n) and the front is O(1).
23+
24+```
25+=+ [i=0 p=0 q=1 r=*(list @ud)]
26+```
27+
28+We are going to need several values throughout the execution of this task so we set them all up here. It's worth noticing that because the subject is searched head first, ignoring faces, we can define a number of faces at once by putting them in a tuple like this.
29+
30+The only one of these that is interesting is `r` We're using `*` to bunt, or get the default value for, the mold `(list @ud)`.
31+
32+```
33+|- ^+ r
34+```
35+
36+Next we define the trap that will contain the majority of our program. We also use `^+` to cast the output by example. This will result in the output being the same type as `r`, a list of `@ud`.
37+
38+```
39+?: =(i n) r
40+```
41+
42+Here is our terminating case, if `i` and `n` are equal we produce `r` as the result.
43+
44+```
45+%= $
46+ i +(i)
47+ p q
48+ q (add p q)
49+ r [q r]
50+==
51+```
52+
53+The final expression in our program calls the `$` arm of the trap we are in with some changes. We increment `i`, set `p` to be `q` and `q` becomes the sum of `q` and `q`. `r` becomes a cell of `q` and whatever `r` was previously. This is the list that will get flopped to produce the result at the end of the computation.