on 657b415 help sign in
urbit.org/docs/programming/deep/4-functions.md
327 lines 9.4 kB history raw
1---
2next: false
3sort: 4
4spam: true
5title: Hoon 101.4: functions
6---
7# Hoon 4: toward actual functions
9In [chapter 0](0-nouns), we read about nouns. In [chapter 1](1-twigs),
10we discovered twigs and legs. In [chapter 2](2-syntax), we learned
11Hoon syntax and created our first source file. In [chapter 3](3-algorithm)
12we wrote an actual algorithm.
14Now we'll go a step farther and actually define a *function*.
16## Goal: `(decrement arg)`
18Of course, Hoon is a functional language and every twig defines a
19function of the subject. But we haven't seen the Hoon equivalent
20of a function *declaration*. Where is Lisp `defun`?
22Also, the Hoon equivalent of a normal function call can't just
23involve compiling the function into a Nock formula, whose subject
24is the function's argument. As Einstein said, everything must be
25as simple as possible, but no simpler.
27A function that worked this way would, by definition, have no
28access to data or code other than the argument. Where would the
29standard library go? Would we have to pass it in the argument?
31## Form of the solution
33Let's get the boilerplate out of the way. We'll keep the same
34external generator interface, but our solution will look like:
36```
37=< :- %say |= [* [[arg=@ud ~] ~]] :- %noun
38 (decrement arg)
39=> ~
40!!
41```
42In place of the `!!`, we'll put a core, effectively a library,
43that exports a function `decrement`. We'll then call that
44function with `(decrement arg)`, an irregular form of
45`%-(decrement arg)`. Again, this core uses `=> ~` to clear
46the subject, so we can't cheat and call `(dec arg)`.
48> `!!`, or "zapzap" or `[%zpzp ~]`, is a twig that
49always crashes. Because it produces the empty span (`%void`), it
50doesn't cause type inference problems.
52## Not quite a function call
54As usual, we'll get to our goal gradually. A first try:
55```
56=< :- %say |= [* [[arg=@ud ~] ~]] :- %noun
57 =+ gat=decrement
58 =<(run gat(sam arg))
59=> ~
60|%
61++ decrement
62 =+ sam=0
63 =+ pre=0
64 |%
65 ++ run
66 ?: =(sam +(pre))
67 pre
68 run(pre +(pre))
69 --
70--
72~tasfyn-partyv:dojo/sandbox> +test 42
7341
74```
75This works, but it's hardly concise or pretty. We'll get there.
77We replaced the `!!` with a `|%` twig that produces a library
78core. The payload of our library core is `~`, because that's the
79subject we sent in. The battery contains one arm, `decrement`.
81The `decrement` arm produces an inner core. The payload of this
82inner core is `[pre=0 sam=0 +>]`, where `+>` is the library core.
83The battery of the inner core has one arm, `run`, which computes
84our algorithm from the last chapter.
86To run our algorithm, we put the core that `decrement` produces
87into a new leg, `gat`. Mutating this core to set `sam` to our
88argument `arg`, we evaluate the `run` arm.
90> Why do we need this `gat` leg? Why can't we just write `=<(run
91decrement(sam arg))`? Remember our `moo:bar.foo` problem from
92the previous chapter. `decrement(sam arg)` mutates not the
93*product* of the decrement arm, but the core that computes it.
94There's no `sam` in that subject, so the twig won't compile.
96## Better is worse
98To make this code look simpler, we need to make it more complex.
99Our next try uses not two nested cores, but *three*:
100```
101=< :- %say |= [* [[arg=@ud ~] ~]] :- %noun
102 =+ gat=decrement
103 =<(run gat(sam arg))
104=> ~
105|%
106++ decrement
107 =+ sam=0
108 |%
109 ++ run
110 =+ pre=0
111 =< loop
112 |%
113 ++ loop
114 ?: =(sam +(pre))
115 pre
116 loop(pre +(pre))
117 --
118 --
119--
120```
121This is actually the final structure of our function, but uses
122none of the syntactic tricks we'll use to make it pretty.
124> Why do we need to make the code more complex? Because we need
125it to use the regular form that our synthetic runes, ie macros,
126use to express function definitions and calls.
128## Loop sugar
130Look at little `loop`. It works just like our old `run`. But
131there's something nice about it: we don't use the symbol `loop`
132anywhere outside these 7 lines of code. It's not exported.
134Therefore, the `loop` name is useless and redundant. Making up
135names is one of the hard problems in computer science, so why
136solve it for no reason?
138So Hoon has an *empty name*: `$`. As a constant, `$` is a
139zero-length symbol (`%$` instead of `%foo`) As a limb is the
140`buc` symbol (`$`).
142Replacing `loop` with `$`, our loop becomes:
143```
144=< $
145|%
146++ $
147 ?: =(sam +(pre))
148 pre
149 $(sam +(run))
150--
151```
153This may not seem like a huge improvement. It's not. But it
154lets us match and move to the synthetic rune `|-`, "barhep":
155```
156|- ?: =(sam +(pre))
157 pre
158 $(pre +(pre))
159```
160`|-` is simply the canonical Hoon loop. All we've done to use a
161core as a loop is to name our arm `$`, and evaluate it on the
162core we just made.
164Our program now:
165```
166=< :- %say |= [* [[arg=@ud ~] ~]] :- %noun
167 =+ gat=decrement
168 =<(run gat(sam arg))
169=> ~
170|%
171++ decrement
172 =+ sam=0
173 |%
174 ++ run
175 =+ pre=0
176 |- ?: =(sam +(pre))
177 pre
178 $(pre +(pre))
179 --
180--
181```
183## Almost to lambda
185Could we use `$` for `++run`? It certainly sounds like the same
186kind of thing as `++loop`. The word "run" just means "do it".
187Why should we be saying "do it" all the time?
189Our third try, with this change:
190```
191=< :- %say |= [* [[arg=@ud ~] ~]] :- %noun
192 =+ gat=decrement
193 =<($ gat(sam arg))
194=> ~
195|%
196++ decrement
197 =| sam=@ud
198 |%
199 ++ $
200 =+ pre=0
201 |- ?: =(sam +(pre))
202 pre
203 $(pre +(pre))
204 --
205--
207~tasfyn-partyv:dojo/sandbox> +test 42
20841
209```
210> Besides `run` to `$`, we changed `=+ sam=0` to `=| sam=@ud`.
211This is some mold magic we'll explain in the next chapter, but
212it basically does the same thing: pushes a leg named `sam`, whose
213value is a number we'll later overwrite.
215This use of `$` in a core, very similar to the way we used `$` in
216our `|-` loop, smells like it too should have a rune. It does:
217```
218=< :- %say |= [* [[arg=@ud ~] ~]] :- %noun
219 =+ gat=decrement
220 =<($ gat(sam arg))
221=> ~
222|%
223++ decrement
224 |= sam=@ud
225 =+ pre=0
226 |- ?: =(sam +(pre))
227 pre
228 $(pre +(pre))
229--
231~tasfyn-partyv:dojo/sandbox> +test 42
23241
233```
234Doesn't `decrement` look like a function? Indeed, we're done with
235the `decrement` arm. This is what it should look like.
237> If you squint a little, `|=` ("bartis") might even be a strange,
238deformed ASCII-art lambda.
240Since it's doing something simple, we might well even compress
241the whole body of `decrement` into one wide-form line:
242```
243=+(pre=0 |-(?:(=(sam +(pre)) pre $(pre +(pre)))))
244```
245This is a bit tight for most. But we can get really compact by
246dropping the labels and going to full geometry:
248```
249=< :- %say |= [* [[arg=@ud ~] ~]] :- %noun
250 =+ gat=decrement
251 =<($ gat(sam arg))
252=> ~
253|%
254++ decrement |=(@ud =+(0 |-(?:(=(+>+< +<) +< $(+< +(+<))))))
255--
256```
257> It's not a good idea to actually program this way. But it does
258strengthen your Hoon tree-geometry muscles. To use tree
259geometry, you have to really know the shape of your subject.
261## Structure of a gate
263What is the noun produced by `decrement`? In true Hoonese it's
264a *gate*, but nobody will hate you for saying "function." And
265while we *slam* our gates, you can feel free to just "call" them.
267Every gate is a core; not every core is a gate. All cores are
268shaped like `[battery payload]`. A core is a gate if its payload
269is a cell, and its battery has one arm, `$`. So a gate is shaped
270like `[formula [sample context]]`.
272To slam (call) a gate, you replace its sample (`+6` or `+<`,
273"lusgal" or "dish") with your argument. "Multiple arguments" are
274a single sample that's a tuple. So our code should actually read
275```
276 =+ gat=decrement
277 =<($ gat(+< arg))
278```
279This `=+` sequence remains ugly. Once again, `decrement(+< arg)`
280is not a mutation to the product of `decrement`, but a mutation
281to the core that produces `decrement`. Fortunately, there's a
282sugary rune that combines the semantics of both these lines:
283```
284 %*($ decrement +< arg))
285```
286This mutates the product of `decrement` as specified. (`+< arg`
287in the wide form of `$*` is just the first of n comma-separated
288pairs; we could write `%*($ decrement +< arg, +> foo, moo 42)`.
290> Arguably, %* is oversweetened; $:(decrement +< arg)
291would be simpler and better. Hoon isn't perfect.
293## Slamming the gate
295Anyway, it shouldn't come as a surprise that a synonym for
296`%*($ function +< argument)` is `%-(function argument)`. And an
297irregular form of this is just `(function argument)`.
299The irregular syntax constructs multiple-argument syntax as a
300tuple; `(add 2 2)` is `(add [2 2])`, `(rsh 3 1 5)` is `(rsh [3 1 5])`.
301If there are no arguments, `(function)`, this means `$:function`,
302ie, `=>(function $)`.
304We can also take the `=> ~` out, since we now know what we're
305doing. The final program:
306```
307=< :- %say |= [* [[arg=@ud ~] ~]] :- %noun
308 (decrement arg)
309|%
310++ decrement
311 |= sam=@ud
312 =+ pre=0
313 |- ?: =(sam +(pre))
314 pre
315 $(pre +(pre))
316--
317```
318## Progress
320In most functional languages, a function is a primitive. In Hoon
321it's a macro, and a pretty complex macro at that. We spent four
322chapters building the concept up out of much smaller pieces.
324Fortunately, once you have functions you can do anything. We
325still have a fair bit to learn, but we've now seen all the
326fundamental building blocks of programming in Hoon. In the next
327chapter, we'll get back into molds and spans.