mirror of
https://github.com/ilyakooo0/urbit.git
synced 2024-12-22 14:21:34 +03:00
323 lines
9.6 KiB
Plaintext
323 lines
9.6 KiB
Plaintext
---
|
|
title: Hoon 101.3: our first program
|
|
sort: 3
|
|
hide: true
|
|
next: false
|
|
---
|
|
# Hoon 101.3: our first program
|
|
|
|
It's time for us to do some actual programming. In this section,
|
|
we'll work through that classic Urbit pons asinorum, decrement.
|
|
|
|
If you learned Nock before Hoon, you've already done decrement.
|
|
If not, all you need to know is that the only arithmetic
|
|
intrinsic in Nock is increment -- in Hoon, the unary `.+` rune.
|
|
So an actual decrement function is required.
|
|
|
|
In chapter 3, we write a decrement builder: more or less the
|
|
simplest nontrivial Urbit program. We should be able to run this
|
|
example:
|
|
```
|
|
~tasfyn-partyv:dojo/sandbox> +test 42
|
|
41
|
|
```
|
|
|
|
## What's in that subject?
|
|
|
|
As we've seen, Hoon works by running a twig against a subject.
|
|
We've been cheerfully running twigs through three chapters while
|
|
avoiding the question: what's in the subject? To avoid the issue
|
|
we've built a lot of constants, etc.
|
|
|
|
Of course your twig's subject comes from whoever runs it. There
|
|
is no one true subject. Our twigs on the command line are not
|
|
run against the same subject as our generator code, even though
|
|
they are both run by the same `:dojo` appliance.
|
|
|
|
But the short answer is that both command-line and builder get
|
|
*basically* the same subject: some ginormous noun containing all
|
|
kinds of bells and whistles and slicers and dicers, including a
|
|
kernel library which can needless to say decrement in its sleep.
|
|
|
|
As yet you have only faced human-sized nouns. We need not yet
|
|
acquaint you with this mighty Yggdrasil, Mother of Trees. First
|
|
we need to figure out what she could even be made of.
|
|
|
|
## Clearing the subject
|
|
|
|
We'll start by clearing the subject:
|
|
```
|
|
:- %say |= * :- %noun
|
|
=> ~
|
|
[%hello %world]
|
|
```
|
|
The `=>` rune ("tisgar"), 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"). In chapter 1, when we wrote
|
|
`+3:test`, we meant `=>(test +3)` or `=<(+3 test)`.)
|
|
|
|
What is this `~`? It's Hoon `nil`, a zero atom with this span:
|
|
```
|
|
~tasfyn-partyv:dojo/sandbox> ?? ~
|
|
[%cube 0 %atom %n]
|
|
~
|
|
```
|
|
We use it for list terminators and the like. Obviously, since
|
|
our old test code is just a constant, a null subject works fine:
|
|
```
|
|
~tasfyn-partyv:dojo/sandbox> +test
|
|
[%hello %world]
|
|
```
|
|
|
|
## Getting an argument
|
|
|
|
Obviously, if we want to write a decrement builder, we'll have to
|
|
get an argument from the command line. This involves changing
|
|
the `test.hoon` boilerplate a little:
|
|
```
|
|
:- %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 replacing a very interesting subject that
|
|
contains `arg` with a very boring one that contains only `arg`,
|
|
for the same reason we cleared the subject with `~`.
|
|
|
|
In case there's any doubt about the subject (`.` is limb syntax
|
|
for `+1`, ie, the whole noun):
|
|
```
|
|
:- %say |= [* [[arg=@ud ~] ~]] :- %noun
|
|
=> arg=arg
|
|
.
|
|
|
|
~tasfyn-partyv:dojo/sandbox> +test 42
|
|
arg=42
|
|
```
|
|
|
|
We can even write a trivial increment function using `.+`:
|
|
```
|
|
:- %say |= [* [[arg=@ud ~] ~]] :- %noun
|
|
=> arg=arg
|
|
+(arg)
|
|
|
|
~tasfyn-partyv:dojo/sandbox> +test 42
|
|
43
|
|
```
|
|
Below we'll skip both boilerplate lines in our examples.
|
|
|
|
## A core is a code-data cell
|
|
|
|
But how do we actually, like, code? The algorithm for decrement
|
|
is clear. We need to count up to 41. (How do we run useful
|
|
programs on a computer with O(n) decrement? That's an
|
|
implementation detail.)
|
|
|
|
We'll need another kind of noun: the *core*. Briefly, the core
|
|
is always a cell `[battery payload]`. The payload is data, the
|
|
battery is code -- one or more Nock formulas, to be exact.
|
|
|
|
Consider a simple core with a one-formula battery. Remember, we
|
|
create Nock formulas by compiling a twig against a subject. The
|
|
subject is dynamic data, but its span is static. What span do we
|
|
give the compiler, and what noun do we give the formula?
|
|
|
|
A core formula always has the core as its subject. The formula
|
|
is essentially a computed attribute on the payload. But if the
|
|
subject was just the payload, the formula couldn't recurse.
|
|
|
|
Of course, there is no need to restrict ourselves to one computed
|
|
attribute. We can just stick a bunch of formulas together and
|
|
call them a battery. The source twigs in this core are called
|
|
"arms," which have labels just like the faces we saw earlier.
|
|
|
|
Hoon overloads computed attributes (arms) and literal attributes
|
|
(legs) in the same namespace. A label in a wing may refer to
|
|
either. To extend the name-resolution tree search described in
|
|
chapter 1, 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.
|
|
|
|
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.
|
|
|
|
This demands an example: if `foo` produces some core `c`, and
|
|
`bar` is an arm in that `c` (which may be `foo` itself, or some
|
|
leg within `foo`), `bar.foo` runs the arm formula with `c` as the
|
|
subject. You might think that `moo.bar.foo` would compute
|
|
`bar.foo`, then search for `moo` within that result. Instead, it
|
|
searches for `moo` within `c`. (You can get the other result
|
|
with `moo:bar.foo`.)
|
|
|
|
Does this sound too tricky? It should - it's about the most
|
|
complicated feature of Hoon. It's all downhill once you
|
|
understand cores.
|
|
|
|
Let's again extend our `++span` mold:
|
|
```
|
|
++ span
|
|
$% [%atom @tas]
|
|
[%cell span span]
|
|
[%core span (map ,@tas twig)]
|
|
[%cube * span]
|
|
[%face @tas span]
|
|
==
|
|
```
|
|
This definition of `%core` is somewhat simplified from the
|
|
reality, but basically conveys it. (Moreover, this version of
|
|
`span` describes every kind of noun we build.) In our `%core` we
|
|
see a payload span and a name-to-twig arm table, as expected.
|
|
|
|
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."
|
|
|
|
## Increment with a core
|
|
|
|
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)`.)
|
|
|
|
We can actually print out a core. Take out the `=< inc`:
|
|
```
|
|
|%
|
|
++ inc
|
|
+(arg)
|
|
--
|
|
|
|
~tasfyn-partyv:dojo/sandbox> +test 42
|
|
!!!
|
|
|
|
~tasfyn-partyv:dojo/sandbox> ? +test 42
|
|
!!!
|
|
```
|
|
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`. Through the magic of labels, 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 actually a simpler way to write this. We've seen it
|
|
already. It's not exactly a variable declaration:
|
|
```
|
|
=+ pre=0
|
|
.
|
|
|
|
~tasfyn-partyv:dojo/sandbox> +test 42
|
|
[pre=0 arg=42]
|
|
```
|
|
|
|
## We actually decrement
|
|
|
|
Now we can write our actual decrement program:
|
|
```
|
|
=+ 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]`.
|
|
|
|
`?:` is a regular rune which does exactly what you think it does.
|
|
Bear in mind, though, that in Hoon 0 (`&`, "pam") is true and 1
|
|
(`|`, "bar") is false.
|
|
|
|
The real action is in `dec(pre +(pre))`. This is obviously an
|
|
irregular form -- it's the same mutation form we saw before.
|
|
Writing it out in full regular form:
|
|
```
|
|
=+ pre=0
|
|
=< dec
|
|
|%
|
|
++ dec
|
|
?: =(arg +(pre))
|
|
pre
|
|
%= dec
|
|
pre +(pre)
|
|
==
|
|
--
|
|
~tasfyn-partyv:dojo/sandbox> +test 42
|
|
41
|
|
```
|
|
`%=`, "centis", is the rune which almost every use of a wing
|
|
resolves to. It might be called "evaluate with changes."
|
|
|
|
When we evaluate with changes, we take a wing (`dec`) here and
|
|
evaluate it as described above. Searching in the subject, which
|
|
is of course our core, we find an arm called `dec` and run it.
|
|
|
|
The changes (replacing `pre` with `+(pre)`) are always applied
|
|
relative to the core we landed on (or the leg we landed on).
|
|
The change wing is relative to this target; the subject of the
|
|
replacement (`+(pre)`) is the original subject.
|
|
|
|
So, in English, we compute the `dec` arm again, against a new
|
|
core with a new payload that contains an incremented `pre`.
|
|
And thus, we decrement. Doesn't seem so hard, does it?
|