mirror of
https://github.com/ilyakooo0/urbit.git
synced 2024-12-23 14:55:48 +03:00
282 lines
8.5 KiB
Plaintext
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]]
|
||
|
```
|