urbit/pub/docs/dev/hoon/principles/3-program.md
2015-12-01 17:43:42 -08:00

9.9 KiB

next: false sort: 3 spam: true title: Hoon 101.3: an algorithm

Hoon 101.3: an algorithm

In chapter 0, we read about nouns. In chapter 1, we discovered twigs and legs. In chapter 2, we learned Hoon syntax and created our first source file.

Now it's time for an actual, meaningful algorithm.

How to use this tutorial

Ideally, you've installed an Urbit planet (if you have a ticket) or comet (if you don't). See the user doc.

We recommend opening up the dojo and just typing the examples; you don't know a language until you know it in your fingers. Also, make sure you've worked through the chapters in order.

Goal: a decrement generator

Our algorithm is the classic Urbit example: decrement.

If you learned Nock before Hoon, you've already written decrement. If not, all you need to know is that the only built-in arithmetic operator in Nock is increment. To decrement, we need to count up to the result with a simple loop.

A practical subject

As we've seen, Hoon works by running a twig against a subject. In chapter 1, we made a simple test subject that was just a few constants, and applied some simple twigs to it.

This time, we'll actually put useful working data in the subject, building up more and more complex subjects as we go.

Subject: nil

We'll start with the empty subject ~:

:-  %say  |=  *  :-  %noun
=>  ~
[%hello %world]

The => rune ("tisgar", %tsgr)), for =>(p q) executes p against the subject, then uses that product as the subject of q.

We've already used an irregular form of =>, or to be more precise its mirror =< ("tisgal", %tsgl). In chapter 1, when we wrote +3:test, we meant =>(test +3) or =<(+3 test).

What is ~? It's Hoon nil, a zero atom:

~tasfyn-partyv:dojo/sandbox> ?? ~
  [%cube 0 [%atom %n]]
~

We use ~ for list terminators and the like. Obviously, since [%hello %world] is just a constant, a nil subject works as well as any:

~tasfyn-partyv:dojo/sandbox> +test
[%hello %world]

Subject: arg=@

Above, we used an empty subject to avoid the very complex, interesting subject your generator twig gets by default.

But a decrement generator needs to get an argument from the command line -- the number we're trying to decrement.

This involves changing the test.hoon boilerplate a little. Again, don't worry about the boilerplate line just yet:

:-  %say  |=  [* [[arg=@ud ~] ~]]  :-  %noun
=>  arg=arg
[%hello arg]

~tasfyn-partyv:dojo/sandbox> +test 42
[%hello 42]

=> arg=arg looks a little odd. We wouldn't ordinarily do this; we're just trying to keep the subject simple.

In case there's any doubt about the subject (remember from chapter 1 that . means +1, the whole subject):

:-  %say  |=  [* [[arg=@ud ~] ~]]  :-  %noun
=>  arg=arg
.

~tasfyn-partyv:dojo/sandbox> +test 42
arg=42

An increment generator

We can even write a trivial increment generator using .+, or even better its irregular form +:

:-  %say  |=  [* [[arg=@ud ~] ~]]  :-  %noun
=>  arg=arg
+(arg)

~tasfyn-partyv:dojo/sandbox> +test 42
43

We'll assume these same first two lines for the rest of the examples in this chapter.

Cores: one more kind of span

To decrement, we need a loop. To write a loop, we need yet another kind of noun: the core. Briefly, a core is a [code data] pair. Our new span mold:

++  span
  $%  [%atom p=@tas]
      [%cell p=span p=span]
      [%core p=span q=(map ,@tas twig)]
      [%cube p=* q=span]
      [%face p=@tas q=span]
  ==

Structure of a core

The core is a cell [battery payload]. The payload is data, the battery is code -- a tree of Nock formulas.

In the %core span, the payload is described by an arbitrary span. The battery is described by a map of symbol to twig.

Yes, the span contains the complete AST of the whole core. This is a very different approach from most typed languages, which compute function signatures. To infer the product of an arm, we retrace the code in theory, but cache in practice.

Arms are computed attributes

How do we use a core? Remember that Nock is a function

Nock(subject formula) -> product

To activate a core, we pull one formula from the battery, and execute with the whole core as the subject. Coincidentally, Nock has one macro instruction, 9, that does just this.

So the formula defines an arbitrary function on the core. The name of this function (as defined in the twig map) is a computed attribute, or arm.

Is a core an object? Not quite, because an arm is not a method. Methods in an OO language have arguments. Arms are functions only of the payload. (A method in Hoon is an arm that produces a gate, which is another core -- but we're getting too far ahead.) However, the battery does look a lot like a classic "vtable."

Arms and wing resolution

It might be a good time to brush up on your chapter 1. The wing resolution algorithm gets a little more complicated.

Hoon overloads computed attributes (arms) and literal attributes (legs) in the same namespace. A label in a wing may refer to either. when searching a core, we look for a matching arm. If we find it we're done. If we don't, or if a ^ mark makes us skip, we search into the payload.

Only the last limb in the wing can activate an arm. If a name resolves to a core arm, but it's not the last limb in the wing, the arm produces the core itself. Similarly, when the wing is not an access but a mutation, the arm refers to the core. Only the end of the wing is activated.

Suppose foo is a core. bar is an arm on foo. Then bar.foo computes the bar arm. Perhaps the product is another core. Even if moo is an arm in this new core, bar.foo, the wing moo.bar.foo does not compute moo on the core bar.foo. Instead, it looks for moo within the payload of the core foo. (You're looking for moo:bar.foo, ie, =>(bar.foo moo).)

Does this sound too tricky? Hopefully not, but it's about the most complicated semantic rule you'll find in Hoon.

Increment with a core

Keeping our two-line boilerplate, let's increment with a core:

=<  inc
|%
++  inc
  +(arg)
--

~tasfyn-partyv:dojo/sandbox> +test 42
43

What's going on? We used the |% rune ("barcen") to produce a core. (There are a lot of runes which create cores; they all start with |, and are basically macros that turn into |%.)

The payload of a core produced with |% is the subject with which |% is compiled. We might say that |% wraps a core around its subject. In this case, the subject of the |%, and thus payload, is our arg=@ud argument.

Then we used this core as the subject of the simple wing inc.

Remember that =<(a b) is just =>(b a). The core is heavy and inc is light, so we use =< to put the heavy part last.

The prettyprinter can even print a core, sort of. Take out the =< inc:

|%
++  inc
  +(arg)
--

~tasfyn-partyv:dojo/sandbox> +test 42
<1.bgq arg=42>

~tasfyn-partyv:dojo/sandbox> ? +test 42
  <1.bgq arg=@ud>
<1.bgq arg=42>

In this notation, 1 means the number of arms in the core, bgq is a very short fingerprint, and arg=42 is the payload (and arg=@ud is the payload span).

Cores can be large and complex, and we obviously can't render all the data in them, either when printing a type or a value. At some point, you'll probably make the mistake of printing a big core, maybe even the whole kernel, as an untyped noun. Just press ^C.

Adding a counter

To decrement, we need to count up to the argument. So we need a counter in our subject, because where else would it go?

Let's change the subject to add a counter, pre:

=>  [pre=0 .]
=<  inc
|%
++  inc
  +(arg)
--

~tasfyn-partyv:dojo/sandbox> +test 42
43

Once again, . is the whole subject, so we're wrapping it in a cell whose head is pre=0. This doesn't change the way we use arg, even though it's one level deeper in the subject tree. Let's look at the subject again:

=>  [pre=0 .]
.

~tasfyn-partyv:dojo/sandbox> +test 42
[pre=0 arg=42]
~tasfyn-partyv:dojo/sandbox> ? +test 42
  [pre=@ud arg=@ud]
[pre=0 arg=42]

There's a less cluttered way to write =>([a .] b). In wide mode, =+(a b). A tall example:

=+  pre=0
.

~tasfyn-partyv:dojo/sandbox> +test 42
[pre=0 arg=42]

This rune =+, "tislus", %tsls, is of course the "variable declaration" we saw in chapter 2.

We actually decrement

Now we can write our actual decrement program, combining the features we've explored above:

=+  pre=0
=<  dec
|%
++  dec
  ?:  =(arg +(pre))
    pre
  dec(pre +(pre))
--

~tasfyn-partyv:dojo/sandbox> +test 42
41

=(a b) is an irregular form of .=(a b), ie, "dottis" or the noun [%dtts a b]. Likewise, +(a) is .+(a), ie, "dotlus" or [%dtls a].

?:, "wuttis", %wtts, does exactly what it does in C.

The real action is in dec(pre +(pre)). We saw this same mutation form in chapter 1. It's an irregular form of the %= rune, "centis", %cnts.

Here, of course, we're computing an arm of a mutated core. We're recomputing the loop arm, dec, against a new core with an incremented pre leg. Basically, everything interesting in Hoon happens when you compute an arm of a mutated core.

The whole program, using only regular forms, wide and tall:

=+  ^=(pre 0)
=<  dec
|%
++  dec
  ?:  .=(arg .+(pre))
    pre
  %=  dec
    pre  .+(pre)
  ==
--
~tasfyn-partyv:dojo/sandbox> +test 42
41

A good illustration of why we need irregular forms.

Progress

Now we've actually done something useful. Well, if you count O(n) decrement as something useful.

We've actually seen most of the major tools in Hoon's toolbox, just in a super-minimalist selection. But we'll need a few more runes to get to something that looks like real programming.

For instance, we have a decrement algorithm, but we don't have a decrement function - in the Hoon sense of the word, anyway. We don't see (dec arg) in this code. That'll be the next chapter.