urbit/pub/docs/dev/hoon/tutorial/4-functions.mdy
2015-11-03 11:49:57 -08:00

268 lines
7.4 KiB
Plaintext

---
title: Hoon 101.4: functions
sort: 4
hide: true
next: false
---
# Hoon 4: toward actual functions
Okay, we've programmed. We've achieved decrement. We've written
what is in some sense a loop. What next?
Well... we're still feeling vaguely disappointed. Because we're
supposed to be doing *functional programming*. And we haven't
yet written any *functions*.
After all, in Hoon we don't really write a command-line utility
to decrement `42`. We write `(dec 42)`. You probably realize
that on the inside, this is not the same thing as a function in a
normal functional language. The Tasmanian tiger is not a tiger.
On the other hand, it certainly *looks* like a function call.
So how do we write the function?
In this chapter, we'll modify `+test` to extend the subject so
that we can write our result as `(dec arg)`. Or rather, `(duck
arg)`, because we want to get out of training wheels and stop
clearing the subject soon.
## Form of the solution
```
=< :- %say |= [* [[arg=@ud ~] ~]] :- %noun
(duck arg)
!! :: some interesting core
```
`!!`, or "zapzap" or `[%zpzp ~]`, can go anywhere a twig can and
always crashes. Because its span is the empty set (`%void`), it
doesn't cause type inference problems.
In place of the `!!`, we'll put a core, effectively a library,
that provides our new, improved decrement function `duck`. We'll
then call it with the irregular form, `(duck arg)`, which looks
like a function call but is in fact some mysterious macro.
## Some interesting core
Translated into imperative programming, what we did in chapter 3
was more like computing a function of a global variable. Now,
we have to actually pass an argument to a function.
Here's our first try:
```
=< :- %say |= [* [[arg=@ud ~] ~]] :- %noun
=+ gat=duck
=<(run gat(sam arg))
=> ~
|%
++ duck
=+ sam=0
=+ pre=0
|%
++ run
?: =(sam +(pre))
pre
run(pre +(pre))
--
--
~tasfyn-partyv:dojo/sandbox> +test 42
41
```
We step back and contemplate our handiwork. Is it good? Well...
it works. Reading programs written without syntactic sugar is
about as fun as eating raw chocolate nibs.
What did we do? In the `duck` arm (we often write `++duck`, for
obvious reasons) we produce a core whose payload is `[pre=0 num=0
~]`, and whose battery contains `++run`.
In the result twig, we first use `++duck` to extend our subject
with a core named `gat`. We then use `run` on that gate. Why do
we need this `gat`? Why can't we just write `=<(run duck(sam
arg))`?
Because the arm is computed *after* the mutation. But here we
need the mutated *result* of `++duck`. Instead, what this code
is doing is trying to mutate `sam` within the core that contains
`++duck`. Where it doesn't exist, so your code won't compile.
And note that with `=<`, we've placed our library structurally
between the original subject and the program we're writing,
but lexically at the bottom with zero left margin. We also
clear the subject to keep things simple.
## A more regular structure
It actually gets worse. To make this code look simpler, we need
to make it more complex. While "function calls" actually fit
quite well into the Hoon architecture, they're also a nontrivial
synthetic construction. We'll build the desugared form the hard
way, then show you where we put the sugar in.
The desugared canonical decrement:
```
=< :- %say |= [* [[arg=@ud ~] ~]] :- %noun
=+ gat=duck
=<(run gat(sam arg))
=> ~
|%
++ duck
=+ sam=0
|%
++ run
=+ pre=0
=< loop
|%
++ loop
?: =(sam +(pre))
pre
loop(pre +(pre))
--
--
--
~tasfyn-partyv:dojo/sandbox> +test 42
41
```
Yuck. Okay, let's fix this.
## Art of the loop
First, look at our little `++loop`. It works just like our old
`++run` loop. We notice that there's actually something nice
about it: we don't use the symbol `loop` anywhere outside these 7
lines of code. It's not exported at all.
Actually, the symbol `loop` name is useless and redundant.
Making up names is one of the hard problems in computer science,
so why solve it? For just this reason, Hoon has an *empty name*,
which as a constant is a zero-length symbol (`%$` instead of
`%foo`), and as a limb is the `buc` symbol (`$`). With `$`,
our loop becomes:
```
=< $
|%
++ $
?: =(sam +(pre))
pre
$(sam +(run))
--
```
This may not seem like a huge improvement. It's not. But it's
exactly equivalent to the synthetic rune `|-`, "bardas":
```
|- ?: =(sam +(pre))
pre
$(pre +(pre))
```
This is obviously the canonical Hoon loop. It leaves us with
```
=< :- %say |= [* [[arg=@ud ~] ~]] :- %noun
=+ gat=duck
=<(run gat(sam arg))
=> ~
|%
++ duck
=+ sam=0
|%
++ run
=+ pre=0
|- ?: =(sam +(pre))
pre
$(pre +(pre))
--
--
~tasfyn-partyv:dojo/sandbox> +test 42
41
```
## Is this a lambda?
Could we use `$` for `++run`? It certainly sounds like the same
kind of thing as `++loop` -- just a word we invented to mean "do
it." Should the programmer have to invent these kinds of words?
```
=< :- %say |= [* [[arg=@ud ~] ~]] :- %noun
=+ gat=duck
=<($ gat(sam arg))
=> ~
|%
++ duck
=| sam=@ud
|%
=+ pre=0
++ $
|- ?: =(sam +(pre))
pre
$(pre +(pre))
--
--
~tasfyn-partyv:dojo/sandbox> +test 42
41
```
(Besides `run` to `$`, we changed `=+ sam=0` to `=| sam=@ud`.
Let's just remember that there's some magic here. We'll come
back and explain it later.)
This is still kind of ugly -- but it's exactly equivalent to
```
=< :- %say |= [* [[arg=@ud ~] ~]] :- %noun
=+ gat=duck
=<($ gat(sam arg))
=> ~
|%
++ duck
|= sam=@ud
=+ pre=0
|- ?: =(sam +(pre))
pre
$(pre +(pre))
--
~tasfyn-partyv:dojo/sandbox> +test 42
41
```
Doesn't that look like a function? Indeed, we're done with
`++duck` -- that's what a Hoon decrement should look like.
If you squint a little, `|=` ("bartis") might even be a strange,
deformed lambda rune.
Since it's doing something simple, we might well even compress
the whole body of the function into one wide-form line:
```
=+(pre=0 |-(?:(=(sam +(pre)) pre $(pre +(pre)))))
```
(According, of course, to taste -- this is a bit tight for some.)
## Gates and how to call them
Our call site remains a disaster, though. We'll need moar sugar.
But first, let's look at this lambda-thing we've made. What is
the noun produced by `++duck`? Our term for it is a "gate," but
nobody will hate you for saying "function." And while we "slam"
our gates, you can feel free to just "call" them.
A gate is a core, of course, but a special kind of core. All
cores are shaped like `[battery payload]`. A gate is shaped like
`[formula [sample context]]`. A gate has one arm, `$`, so its
battery is just a formula. To slam a gate, you replace its
sample (`+6` or `+<`, "luslit" or "lust") with your own noun,
and apply the formula to the mutated gate.
As we explained earlier, `duck(sam arg)` is not the right way to
mutate the gate we make with `duck`, because it's actually
trying to mutate the core we used to make `duck`. But there has
to be some sugar to do this, and there is: `%*`, "centar". We
can replace our call site with `%*($ duck sam arg)`.
This is also not quite orthodox, because the whole point of a
gate is the canonical shape that defines a calling convention.
We can and should say: `%*($ duck +< arg)`.
Unsurprisingly, this in turn is `%-(duck arg)` in regular form,
or `(duck arg)`