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

282 lines
8.5 KiB
Plaintext

---
title: Hoon 101.1: twigs and legs
sort: 1
hide: true
next: false
---
# Hoon 101.1: twigs and legs
In the last chapter we learned how to make nouns. In this
chapter we'll start programming a little.
Reminder: we nest large digressions in curly braces. If you see
a {paragraph} or two, assume it's of interest to language nerds
only, and "guaranteed not on the test."
## Nock for Hoon programmers
Hoon compiles itself to a pico-interpreter called Nock, a
combinator algebra defined in 200 words. This isn't the place to
explain Nock (which is to Hoon much as assembly language is to
C), but Nock is just a way to express a function as a noun.
Specifically, you can think of Nock as a (Turing-complete)
interpreter shaped like (pseudocode):
```
Nock(problem) => product
```
This `problem` is always a cell `[subject formula]`. The
function is `formula`. The input to the function is the noun
`subject`. The output is the noun `product`.
(Why is Nock `[subject formula]` rather than `[formula subject]`?
Or, to use more familiar terminology, why `[argument function]`
rather than `[function argument]`? For no good reason, but it
doesn't really matter in practice.)
## From Hoon to Nock
The Hoon parser turns an source expression (even one as simple as
`42` from the last chapter) into a noun called a `twig`. If you
know what an AST is, a twig is an AST. (If you don't know what
an AST is, you probably don't have any student loans.)
To simplify slightly, the Hoon compiler is shaped like:
```
Hoon(subject-span function-twig) => [product-span formula-nock]
```
Hoon, like Nock, is a *subject-oriented* language. Your code is
always executed against one input noun, the subject. For any
subject noun in `subject-span` (ie, argument type), the compiler
produces a Nock formula that computes `function-twig` on that
subject, and a `product-span` that is the span of the product
(ie, result type).
{This is really a nontrivial difference. In a normal,
non-subject-oriented language, your code executes against a
scope, stack, environment, or other variable context, probably
not even a regular user-level value. For ordinary coders, "SOP"
is one of the hardest things to understand about Hoon; for some
reason, your brain keeps wanting the interpreter to be more
complicated. There is of course a stack in a Nock interpreter,
but solely for reduction state; actually, you can build a Nock
that uses the C stack only, but still provides perfect TCO.}
## From constants to twigs
In the last chapter we were entering degenerate twigs like `42`.
Obviously a numeric constant doesn't use the subject at all, so
it's not a very interesting example of SOP.
Let's use the dojo variable facility (this is *not* Hoon syntax,
just a dojo command) to make a test subject:
```
~tasfyn-partyv:dojo> =test [[[8 9] 5] [6 7]]
```
The `=test` command tells the dojo to rearrange its stock subject
to include this `test` noun. Let's check that it's there:
```
~tasfyn-partyv:dojo> test
[[[8 9] 5] 6 7]
```
{If you're wondering why `[6 7]` got printed as `6 7`, remember
that `[]` associates to the right.}
We want to use `test`, this harmless little noun, as the subject
for some equally harmless twigs. We can do this with the `:`
syntax, which composes twigs in the functional sense. The twig
`a:b` uses the product of twig `b` as the subject of twig `a`:
```
~tasfyn-partyv:dojo> 42:test
42
~tasfyn-partyv:dojo> 42:420
42
```
## Tree addressing
The simplest twigs produce a subtree, or "leg", of the subject.
A cell, of course, is a binary tree. The very simplest twig is
`.`, which produces the root of the tree - the whole subject:
```
~tasfyn-partyv:dojo> .:test
[[[8 9] 5] 6 7]
```
Like human languages, Hoon is full of irregular abbreviations.
The `.` syntax is a shorthand for `+1`:
```
~tasfyn-partyv:dojo> +1:test
[[[8 9] 5] 6 7]
```
Hoon has a simple tree addressing scheme (inherited from Nock):
the root is `1`, the head of `n` is `2n`, the tail is `2n+1`.
The twig syntax for a tree address is `+n`.
In our example noun, each leaf is its own tree address:
```
~tasfyn-partyv:dojo> +2:test
[[8 9] 5]
~tasfyn-partyv:dojo> +3:test
[6 7]
~tasfyn-partyv:dojo> +4:test
[8 9]
~tasfyn-partyv:dojo> +5:test
5
~tasfyn-partyv:dojo> +6:test
6
~tasfyn-partyv:dojo> +7:test
7
```
{An instinct for binary tree geometry develops over time as you
use the system, rather the way most programmers learn to do
binary math. No, really.}
## Lark syntax
This alternative syntax for a tree address maps noun geometry
directly to a glyph, skipping numbers. Lark syntax creates a
recognizable geometric shape by alternating between two head/tail
pairs, read left to right: `-` and `+`, `<` and `>`.
Thus `-` is `+2`, `+` is `+3`, `+<` is `+6`, `->` is `+5`, `-<+`
is `+9`, etc.
{Why lark syntax? Code full of numbers is ugly and distracting,
and looks like hardcoded constants. We actually almost never use
the `+` syntax.}
## Simple faces
Tree addressing is cool, but it would be pretty tough to program
in Hoon if it was the only way of getting data out of a subject.
Let's introduce some new syntax:
```
~tasfyn-partyv:dojo> foo=42
foo=42
~tasfyn-partyv:dojo> ? foo=42
foo=@ud
foo=42
~tasfyn-partyv:dojo> ?? foo=42
[%face %foo [%atom %ud]]
foo=42
```
To extend our `++span` mold:
```
++ span
$% [%atom p=@tas]
[%cell p=span p=span]
[%cube p=* q=span]
[%face p=@tas q=span]
==
```
The `%face` span wraps a label around a noun. Then we can
get a leg by name. Let's make a new dojo variable:
```
~tasfyn-partyv:dojo> =test [[[8 9] 5] foo=[6 7]]
```
The syntax is what you might expect:
```
~tasfyn-partyv:dojo> test
[[[8 9] 5] foo=[6 7]]
~tasfyn-partyv:dojo> foo:test
[6 7]
```
Does this do what you expect it to do?
```
~tasfyn-partyv:dojo> +3:test
foo=[6 7]
~tasfyn-partyv:dojo> ? +3:test
foo=[@ud @ud]
foo=[6 7]
~tasfyn-partyv:dojo> ?? +3:test
[%face %foo [%cell [%atom %ud] [%atom %ud]]]
foo=[6 7]
```
## Interesting faces; wings
Again, you're probably used to name resolution in variable scopes
and flat records, but not in trees. (Partly this is because the
tradition in language design is to eschew semantics that make it
hard to build simple symbol tables, because linear search of a
nontrivial tree is a bad idea on '80s hardware.}
Let's look at a few more interesting face cases. First, suppose
we have two cases of `foo`?
```
~tasfyn-partyv:dojo> =test [[foo=[8 9] 5] foo=[6 7]]
~tasfyn-partyv:dojo> foo:test
[8 9]
```
In the tree search, the head wins. We can overcome this with a
`^` prefix, which tells the search to skip its first hit:
```
~tasfyn-partyv:dojo> ^foo:test
[6 7]
```
`^^foo` will skip two foos, `^^^foo` three, *ad infinitum*.
But what about nested labels?
```
~tasfyn-partyv:dojo> =test [[[8 9] 5] foo=[6 bar=7]]
~tasfyn-partyv:dojo> bar:test
/~tasfyn-partyv/home/~2015.9.16..21.40.21..1aec:<[1 1].[1 9]>
-find-limb.bar
find-none
```
We can't search *through* a label. If we want to get our `bar`
out, we need to search *into* it:
```
~tasfyn-partyv:dojo> bar.foo:test
7
```
`bar.foo` is what we call a `wing`, a search path in a noun.
Note that the wing runs from left to right, ie, the opposite of
most languages: `bar.foo` means "bar within foo."
Each step in a wing is a `limb`. {Most languages use metaphors;
Hoon abuses them.} A limb can be a tree address, like `+3` or
`.`, or a label like `foo`. We can combine them in one wing:
```
~tasfyn-partyv:dojo> bar.foo.+3:test
7
```
It's important to note the difference between `bar.foo:test`
and `bar:foo:test`, even though they produce the same product:
```
~tasfyn-partyv:dojo> bar:foo:test
7
```
`bar.foo` is one twig, which we run on the product of `test`.
That's different from running `bar` on the product of `foo` on
the product of `test`.
## Mutation
Mutation? Well, not really. We can't modify nouns; the concept
doesn't even make sense in Hoon.
Rather, we build new nouns which are {logical -- the pointers are
shared, of course} copies of old ones, but with mutations. Let's
build a "mutated" copy of our test noun:
```
~tasfyn-partyv:dojo> test
[[[8 9] 5] foo=[6 bar=7]]
~tasfyn-partyv:dojo> test(foo 42)
[[[8 9] 5] foo=42]
~tasfyn-partyv:dojo> test(+8 %eight, bar.foo [%hello %world])
[[[%eight 9] 5] foo=[6 [%hello %world]]]
```
As we see, there's no need for the mutant noun to be shaped
anything like the old noun. They're different nouns.
A mutation, like `+8 %eight`, specifies a wing and a twig.
The wing, like `+8` or `bar.foo`, defines a leg to replace.
The twig runs against the original subject.
Can we use mutation to build a cyclical noun? Nice try, but no:
```
~tasfyn-partyv:dojo> test(+8 test)
[[[[[[8 9] 5] foo=[6 bar=7]] 9] 5] foo=[6 bar=7]]
```