2015-11-03 22:49:57 +03:00
|
|
|
---
|
|
|
|
title: Hoon 101.4: functions
|
|
|
|
sort: 4
|
|
|
|
hide: true
|
|
|
|
next: false
|
|
|
|
---
|
2015-09-30 00:36:58 +03:00
|
|
|
# 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)`
|