mirror of
https://github.com/urbit/shrub.git
synced 2024-12-14 20:02:51 +03:00
Merge remote-tracking branch 'curtis/newdoc'
This commit is contained in:
commit
c9d45b4d72
@ -7,8 +7,9 @@ sort: 2
|
|||||||
|
|
||||||
# Developer documentation
|
# Developer documentation
|
||||||
|
|
||||||
Urbit has three programming layers: Nock (combinator nano-VM),
|
Urbit has three programming layers: [Nock](dev/nock) (combinator nano-VM),
|
||||||
Hoon (strict functional language), and Arvo (functional OS).
|
[Hoon](dev/hoon) (strict functional language), and [Arvo](dev/arvo) (functional
|
||||||
|
OS).
|
||||||
|
|
||||||
To code in Urbit, the least you need to learn is Hoon, plus a
|
To code in Urbit, the least you need to learn is Hoon, plus a
|
||||||
little bit of Arvo. Nock is a sort of functional assembly
|
little bit of Arvo. Nock is a sort of functional assembly
|
||||||
|
@ -7,8 +7,7 @@ sort: 3
|
|||||||
|
|
||||||
# Arvo
|
# Arvo
|
||||||
|
|
||||||
Arvo is a functional operating system.
|
Arvo is a functional operating system. But hopefully you knew that! Sorry,
|
||||||
|
please watch this space for actual documentation.
|
||||||
Watch this space for actual documentation.
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -8,8 +8,8 @@ sort: 2
|
|||||||
|
|
||||||
# Hoon
|
# Hoon
|
||||||
|
|
||||||
Hoon is a strict, typed, pure functional language.
|
Hoon is a strict, typed, pure functional language.
|
||||||
|
|
||||||
Watch this space for actual documentation.
|
We're still updating the docs; start with the tutorial, ["Hoon 101"](hoon/tutorial).
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,4 +0,0 @@
|
|||||||
Tutorials
|
|
||||||
=========
|
|
||||||
|
|
||||||
<list dataPreview="true" titlesOnly="true"></list>
|
|
10
pub/docs/dev/hoon/tutorial.mdy
Normal file
10
pub/docs/dev/hoon/tutorial.mdy
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
---
|
||||||
|
logo: black
|
||||||
|
title: Hoon 101
|
||||||
|
sort: 2
|
||||||
|
---
|
||||||
|
|
||||||
|
Welcome to Hoon 101! This tutorial is still under construction;
|
||||||
|
check back later for more installments.
|
||||||
|
|
||||||
|
<list></list>
|
@ -1,232 +0,0 @@
|
|||||||
# Hoon 0: introduction
|
|
||||||
|
|
||||||
Hoon is a strict, higher-order typed pure-functional language.
|
|
||||||
|
|
||||||
Why Hoon? On the one hand, typed functional languages are known
|
|
||||||
for a particularly pleasant phenomenon: once your code compiles,
|
|
||||||
it's quite likely to work. On the other hand, most typed
|
|
||||||
functional languages are influenced by advanced mathematics.
|
|
||||||
As Barbie once put it, math class is hard.
|
|
||||||
|
|
||||||
Hoon is a typed FP language for the common street programmer.
|
|
||||||
Well-written Hoon is as concrete and data-oriented as possible.
|
|
||||||
The less functional magic you use, the better. One Haskell
|
|
||||||
hacker described Hoon as "imperative programming in a functional
|
|
||||||
language." He didn't mean this as a compliment, but we choose to
|
|
||||||
take it as one.
|
|
||||||
|
|
||||||
Moreover, one task of a type system in network computing is
|
|
||||||
marshalling typed data on the sender, and validating untrusted
|
|
||||||
data on the receiver. Hoon is very good at this task, which in
|
|
||||||
most typed languages is an afterthought at best.
|
|
||||||
|
|
||||||
The main disadvantage of Hoon is that its syntax and semantics
|
|
||||||
are unfamiliar. The syntax will remind too many of Perl, but
|
|
||||||
like most human languages (and unlike Perl) it combines a regular
|
|
||||||
core structure with irregular variations. Its semantic
|
|
||||||
complexity is bounded by the fact that the compiler is only 2000
|
|
||||||
lines of Hoon (admittedly an expressive language). Most peoples'
|
|
||||||
experience is that Hoon is much easier to learn than it looks.
|
|
||||||
|
|
||||||
## Nouns: data made boring
|
|
||||||
|
|
||||||
A noun is an atom or a cell. An atom is any unsigned integer. A
|
|
||||||
cell is an ordered pair of nouns.
|
|
||||||
|
|
||||||
The noun is an intentionally boring data model. Nouns (at least,
|
|
||||||
nouns in Urbit) don't have cycles (although a noun implementation
|
|
||||||
should take advantage of acyclic graph structure). Noun
|
|
||||||
comparison is always by value (there is no way for the programmer
|
|
||||||
to test pointer equality). Nouns are strict; there is no such
|
|
||||||
thing as an infinite noun. And, of course, nouns are immutable.
|
|
||||||
So there's basically no way to have any real fun with nouns.
|
|
||||||
|
|
||||||
For language historians, nouns are Lisp's S-expressions, minus a
|
|
||||||
lot of hacks, tricks, and features that made sense 50 years ago.
|
|
||||||
In particular, because atoms are not tagged (an atom can encode a
|
|
||||||
string, for instance), nouns work best with a static type system.
|
|
||||||
How do you print an atom if you don't know whether it's a string
|
|
||||||
or a number? You can guess, but...
|
|
||||||
|
|
||||||
## A type system for nouns
|
|
||||||
|
|
||||||
So learning nouns in practice involves learning them with a type
|
|
||||||
system that makes them usable. Fortunately, we have that.
|
|
||||||
|
|
||||||
One obstacle to learning Hoon is that it has two quite distinct
|
|
||||||
concepts that might equally be called a "type." Worse, most
|
|
||||||
other typed functional languages are mathy and share a basically
|
|
||||||
mathematical concept of "type." We can't avoid using the T-word
|
|
||||||
occasionally, but it has no precise meaning in Hoon and can be
|
|
||||||
extremely confusing.
|
|
||||||
|
|
||||||
Hoon's two kinds of "type" are `span` and `mold`. A span is both
|
|
||||||
a constructively defined set of nouns, and a semantic convention
|
|
||||||
for users in that set. A `mold` is a function whose range is
|
|
||||||
some useful span. A mold is always idempotent (for any noun x,
|
|
||||||
`f(x)` equals `f(f(x))`), and its domain is any noun.
|
|
||||||
|
|
||||||
(One way to explain this is that while a span is what most
|
|
||||||
languages call a "type," Hoon has no way for the programmer to
|
|
||||||
express a span directly. Instead, we use inference to define it
|
|
||||||
as the range of a function. This same function, the mold, can
|
|
||||||
also be used to validate or normalize untrusted, untyped data --
|
|
||||||
a common problem in modern programming.)
|
|
||||||
|
|
||||||
(Hoon's inference algorithm is somewhat dumber than the
|
|
||||||
unification algorithms (Hindley-Milner) used in most typed
|
|
||||||
functional languages. Hoon reasons only forward, not backward.
|
|
||||||
It needs more manual annotations, which you usually want anyway.
|
|
||||||
Otherwise, it gets more or less the same job done.)
|
|
||||||
|
|
||||||
## Let's make some nouns
|
|
||||||
|
|
||||||
This stuff isn't even slightly hard. Let's make a noun:
|
|
||||||
```
|
|
||||||
~tasfyn-partyv:dojo> 42
|
|
||||||
```
|
|
||||||
You'll see the expression you entered, then the resulting value:
|
|
||||||
```
|
|
||||||
> 42
|
|
||||||
42
|
|
||||||
```
|
|
||||||
Let's try a different value:
|
|
||||||
```
|
|
||||||
~tasfyn-partyv:dojo> 0x2a
|
|
||||||
```
|
|
||||||
You'll see:
|
|
||||||
```
|
|
||||||
> 0x2a
|
|
||||||
0x2a
|
|
||||||
```
|
|
||||||
`42` and `0x2a` are actually *the same noun*, because they're the
|
|
||||||
same number. But we don't just have the noun to print - we have
|
|
||||||
a `[span noun]` cell (sometimes called a `vase`).
|
|
||||||
|
|
||||||
As you recall, a span defines a set of nouns and a semantic
|
|
||||||
interpretation. As sets, both spans here are "any number". But
|
|
||||||
semantically, `42` has a decimal span and `0x2a` hexadecimal, so
|
|
||||||
they print differently.
|
|
||||||
|
|
||||||
(It's important to note that Hoon is a statically typed language.
|
|
||||||
We don't work with vases unless we're dynamically compiling code,
|
|
||||||
which is of course what we're doing here in the shell. Dynamic
|
|
||||||
type is static type compiled at runtime.)
|
|
||||||
|
|
||||||
Finally, let's make some cells. Try these on your own ship:
|
|
||||||
```
|
|
||||||
~tasfyn-partyv:dojo> [42 0x2a]
|
|
||||||
~tasfyn-partyv:dojo> [42 [0x2a 420]]
|
|
||||||
~tasfyn-partyv:dojo> [42 0x2a 420]
|
|
||||||
```
|
|
||||||
We observe that cells associate right: `[a b c]` is just another
|
|
||||||
way of writing `[a [b c]]`.
|
|
||||||
|
|
||||||
Also, Lisp veterans beware: Hoon `[a b]` is Lisp `(a . b)`, Lisp
|
|
||||||
`(a b)` is Hoon `[a b ~]`(`~` represents nil, with a value of atom `0`). Lisp and Hoon are both pair-oriented
|
|
||||||
languages down below, but Lisp has a layer of sugar that makes it
|
|
||||||
look list-oriented. Hoon loves its "improper lists," ie, tuples.
|
|
||||||
|
|
||||||
## Looking at spans
|
|
||||||
|
|
||||||
What are these mysterious spans? We can see them with the `?`
|
|
||||||
prefix, which prints the span along with the result. Moving to
|
|
||||||
a more compact example format:
|
|
||||||
```
|
|
||||||
~tasfyn-partyv:dojo> ? 42
|
|
||||||
@ud
|
|
||||||
42
|
|
||||||
~tasfyn-partyv:dojo> ? 0x2a
|
|
||||||
@ux
|
|
||||||
0x2a
|
|
||||||
```
|
|
||||||
`@ud` and `@ux` stand for "unsigned decimal" and "unsigned hex,"
|
|
||||||
obviously. But what is this syntax?
|
|
||||||
|
|
||||||
We only derive spans through inference. So there's no language
|
|
||||||
syntax for a span. We have to be able to print spans, though, if
|
|
||||||
only for debugging and diagnostics. `@ud` is an print-only
|
|
||||||
syntax. (In this case it happens to be the same as the `mold`
|
|
||||||
syntax, but that's just a coincidence.)
|
|
||||||
|
|
||||||
## Looking at spans, part 2
|
|
||||||
|
|
||||||
A good way to teach yourself to think in nouns is to look not at
|
|
||||||
the prettyprinted span, but at the actual noun it's made of.
|
|
||||||
Since everything in Hoon is a noun, a span is a noun too. When
|
|
||||||
we use `??` rather than `?` as a prefix, we see the noun:
|
|
||||||
```
|
|
||||||
~tasfyn-partyv:dojo> ?? 42
|
|
||||||
[%atom %ud]
|
|
||||||
42
|
|
||||||
~tasfyn-partyv:dojo> ?? [42 0x2a]
|
|
||||||
[%cell [%atom %ud] [%atom %ux]]
|
|
||||||
[42 0x2a]
|
|
||||||
```
|
|
||||||
What is this `%atom` notation? Is it a real noun? Can anyone
|
|
||||||
make one?
|
|
||||||
```
|
|
||||||
~tasfyn-partyv:dojo> %atom
|
|
||||||
%atom
|
|
||||||
~tasfyn-partyv:dojo> %foo
|
|
||||||
%foo
|
|
||||||
~tasfyn-partyv:dojo> [%foo %bar]
|
|
||||||
[%foo %bar]
|
|
||||||
```
|
|
||||||
What if we look at the span?
|
|
||||||
```
|
|
||||||
~tasfyn-partyv:dojo> ? %foo
|
|
||||||
%foo
|
|
||||||
%foo
|
|
||||||
~tasfyn-partyv:dojo> ?? %foo
|
|
||||||
[%cube 7.303.014 %atom %tas]
|
|
||||||
%foo
|
|
||||||
```
|
|
||||||
This takes a little bit of explaining. First of all, `7.303.014`
|
|
||||||
is just the German (and Urbit) way of writing `7,303,014`, or the
|
|
||||||
hexadecimal number `0x6f.6f66`, or the string "foo" as an
|
|
||||||
unsigned integer. (It's much easier to work with large integers
|
|
||||||
when the digits are grouped.) Second, remembering that cells
|
|
||||||
nest right, `[%cube 7.303.014 %atom %tas]` is really `[%cube
|
|
||||||
7.303.014 [%atom %tas]]`.
|
|
||||||
|
|
||||||
A `%cube` span is a constant -- a set of one noun, the atom
|
|
||||||
`7.303.014`. But we still need to know how to print that noun.
|
|
||||||
In this case, it's an `[%atom %tas]`, ie, a text symbol.
|
|
||||||
|
|
||||||
Cubes don't have to be symbols -- in fact, we can take the
|
|
||||||
numbers we've just been using, and make them constants:
|
|
||||||
```
|
|
||||||
~tasfyn-partyv:dojo> %42
|
|
||||||
%42
|
|
||||||
~tasfyn-partyv:dojo> ? %42
|
|
||||||
%42
|
|
||||||
%42
|
|
||||||
~tasfyn-partyv:dojo> ?? %42
|
|
||||||
[%cube 42 %atom %ud]
|
|
||||||
%42
|
|
||||||
```
|
|
||||||
|
|
||||||
## Our first mold
|
|
||||||
|
|
||||||
After seeing a few span examples, are we ready to describe the
|
|
||||||
set of all spans with a Hoon mold? Well, no, but let's try it
|
|
||||||
anyway. Ignore the syntax (which we'll explain later; this is a
|
|
||||||
tutorial, not a reference manual), and you'll get the idea:
|
|
||||||
```
|
|
||||||
++ span
|
|
||||||
$% [%atom @tas]
|
|
||||||
[%cell span span]
|
|
||||||
[%cube * span]
|
|
||||||
==
|
|
||||||
```
|
|
||||||
This mold is not the entire definition of `span`, just the cases
|
|
||||||
we've seen so far. In English, a valid span is either:
|
|
||||||
|
|
||||||
- a cell with head `%atom`, and tail some symbol.
|
|
||||||
- a cell with head `%cell`, and tail some pair of spans.
|
|
||||||
- a cell with head `%cube`, and tail a noun-span pair.
|
|
||||||
|
|
||||||
The head of a span is essentially the tag in a variant record,
|
|
||||||
a pattern every programming language has. To use the noun, we
|
|
||||||
look at the head and then decide what to do with the tail.
|
|
272
pub/docs/dev/hoon/tutorial/0-nouns.mdy
Normal file
272
pub/docs/dev/hoon/tutorial/0-nouns.mdy
Normal file
@ -0,0 +1,272 @@
|
|||||||
|
---
|
||||||
|
title: Hoon 101.0: nouns, spans, and molds
|
||||||
|
sort: 0
|
||||||
|
next: true
|
||||||
|
---
|
||||||
|
|
||||||
|
# Hoon 101.0: nouns, spans and molds
|
||||||
|
|
||||||
|
Hoon is a strict, higher-order typed pure-functional language.
|
||||||
|
|
||||||
|
Why Hoon? Typed functional languages are known for a pleasant
|
||||||
|
phenomenon: once your code compiles, it's quite likely to work.
|
||||||
|
But most typed functional languages are conceptually dependent on
|
||||||
|
abstract advanced math, and difficult to understand without it.
|
||||||
|
|
||||||
|
Hoon is a typed FP language for the common street programmer.
|
||||||
|
Well-written Hoon is as concrete and data-oriented as possible.
|
||||||
|
The less functional magic you use, the better. But the magic is
|
||||||
|
there, mostly, if you need it.
|
||||||
|
|
||||||
|
The main disadvantage of Hoon is that its syntax and semantics
|
||||||
|
are unfamiliar. The syntax will remind too many of Perl, but
|
||||||
|
like most human languages (and unlike Perl) it combines a regular
|
||||||
|
core structure with irregular variations. And Hoon's semantic
|
||||||
|
is bounded by the size of the compiler: type inference plus code
|
||||||
|
generation are 2000 lines of Hoon. Most peoples' experience is
|
||||||
|
that the language is much easier to learn than it looks. Not
|
||||||
|
that it looks easy!
|
||||||
|
|
||||||
|
> The name "Hoon" is from the Wallace Stevens poem, _Tea at the
|
||||||
|
Palaz of Hoon_. It also means "hooligan" in Australian.
|
||||||
|
|
||||||
|
## Nouns: data made boring
|
||||||
|
|
||||||
|
A noun is an atom or a cell. An atom is any unsigned integer. A
|
||||||
|
cell is an ordered pair of nouns.
|
||||||
|
|
||||||
|
The noun is an intentionally boring data model. Nouns don't have
|
||||||
|
cycles (although a noun implementation should take advantage of
|
||||||
|
acyclic graph structure). Noun comparison is always by value
|
||||||
|
(there is no way for the programmer to test pointer equality).
|
||||||
|
Nouns are strict; there is no such thing as an infinite noun.
|
||||||
|
And, of course, nouns are immutable. There's basically no way to
|
||||||
|
have any real fun with nouns.
|
||||||
|
|
||||||
|
> Nouns are Lisp's S-expressions, minus a lot of hacks, tricks,
|
||||||
|
and features that made sense 50 years ago. In particular,
|
||||||
|
because atoms are not tagged (an atom can encode a string, for
|
||||||
|
instance), nouns depend on a static type system at a higher
|
||||||
|
layer. How do you print an atom if you don't know whether it's a
|
||||||
|
string or a number?
|
||||||
|
|
||||||
|
## A type system for nouns
|
||||||
|
|
||||||
|
One obstacle to learning Hoon is that it has two quite distinct
|
||||||
|
concepts that might equally be called a "type." Worse, most
|
||||||
|
other typed functional languages are mathy and share a basically
|
||||||
|
mathematical concept of "type." Hoon does not have this concept
|
||||||
|
at all. We can't avoid using the T-word occasionally, but it has
|
||||||
|
no precise meaning in Hoon and can be extremely confusing.
|
||||||
|
|
||||||
|
Hoon's two kinds of "type" are `span` and `mold`. A span is both
|
||||||
|
a constructively defined set of nouns, and a semantic convention
|
||||||
|
for users in that set. A `mold` is a function whose range is
|
||||||
|
some useful span. A mold is always idempotent (for any noun `x`,
|
||||||
|
`f(x)` equals `f(f(x))`), and its domain is any noun.
|
||||||
|
|
||||||
|
One way to explain this is that while a span is what most
|
||||||
|
languages call a "type," Hoon has no syntax for the programmer to
|
||||||
|
define a span directly. Instead, we use inference to define it
|
||||||
|
as the range of a mold function. This mold can also be used to
|
||||||
|
validate or normalize untrusted, untyped data -- a common problem
|
||||||
|
in modern programming, because networks.
|
||||||
|
|
||||||
|
Hoon's inference algorithm is dumber than the unification
|
||||||
|
algorithms (Hindley-Milner) used in most typed functional
|
||||||
|
languages. Hoon thinks only forward, not backward. Eg, Haskell
|
||||||
|
can infer the result type of a function from its argument
|
||||||
|
(forward), or the argument type from the result (backward).
|
||||||
|
Hoon can do the first but not the second.
|
||||||
|
|
||||||
|
So Hoon needs more manual typecasts, which you usually want
|
||||||
|
anyway for prosaic software-engineering reasons. Otherwise its
|
||||||
|
typesystem solves more or less the same job, including
|
||||||
|
pattern-matching, genericity / typeclasses, etc.
|
||||||
|
|
||||||
|
> Sending a noun over the network is a good example of why Hoon
|
||||||
|
is different. In a normal modern language, you serialize and
|
||||||
|
deserialize a data type by extending your type to implement a
|
||||||
|
serialization interface. In Hoon, any value is just a noun, so
|
||||||
|
we have one function (`jam`) that converts any noun to an atom,
|
||||||
|
and another (`cue`) that is its inverse. To validate, the
|
||||||
|
receiver runs its own mold on the cued noun, and we've sent typed
|
||||||
|
data over the network without any attack surface (except `jam`
|
||||||
|
and `cue`, which fit on a page). No custom serialization methods
|
||||||
|
are required, and the mold itself is never sent; protocol
|
||||||
|
agreement is out of band.
|
||||||
|
|
||||||
|
## Let's make some nouns
|
||||||
|
|
||||||
|
Nouns aren't even slightly hard. Let's make a noun:
|
||||||
|
```
|
||||||
|
~tasfyn-partyv:dojo> 42
|
||||||
|
```
|
||||||
|
You'll see the expression you entered, then the result:
|
||||||
|
```
|
||||||
|
> 42
|
||||||
|
42
|
||||||
|
```
|
||||||
|
Let's try a different value:
|
||||||
|
```
|
||||||
|
~tasfyn-partyv:dojo> 0x2a
|
||||||
|
```
|
||||||
|
You'll see:
|
||||||
|
```
|
||||||
|
> 0x2a
|
||||||
|
0x2a
|
||||||
|
```
|
||||||
|
`42` and `0x2a` are actually *the same noun*, because they're the
|
||||||
|
same number. But we don't just have the noun to print - we have
|
||||||
|
a `[span noun]` cell (sometimes called a `vase`).
|
||||||
|
|
||||||
|
As you recall, a span defines a set of nouns and a semantic
|
||||||
|
interpretation. As sets, both spans here are "any number". But
|
||||||
|
semantically, `42` has a decimal span and `0x2a` hexadecimal, so
|
||||||
|
they print differently.
|
||||||
|
|
||||||
|
> It's important to remember that Hoon is a statically typed language.
|
||||||
|
We don't work with vases unless we're dynamically compiling code,
|
||||||
|
which is of course what we're doing here in the dojo. In Hoon,
|
||||||
|
dynamic type equals static type plus runtime compilation.
|
||||||
|
|
||||||
|
Let's make some cells. Try these on your own urbit:
|
||||||
|
```
|
||||||
|
~tasfyn-partyv:dojo> [42 0x2a]
|
||||||
|
~tasfyn-partyv:dojo> [42 [0x2a 420]]
|
||||||
|
~tasfyn-partyv:dojo> [42 0x2a 420]
|
||||||
|
```
|
||||||
|
We observe that cells associate right: `[a b c]` is just another
|
||||||
|
way of writing `[a [b c]]`.
|
||||||
|
|
||||||
|
> Lisp veterans beware: Hoon `[a b]` is Lisp `(a . b)`, Lisp
|
||||||
|
`(a b)` is Hoon `[a b ~]`(`~` represents nil, with a value of
|
||||||
|
atom `0`). Lisp and Hoon are both pair-oriented languages down
|
||||||
|
below, but Lisp has a layer of sugar that makes it look
|
||||||
|
list-oriented. Hoon loves its "improper lists," ie, tuples.
|
||||||
|
|
||||||
|
## Looking at spans
|
||||||
|
|
||||||
|
What are these mysterious spans? We can see them with the `?`
|
||||||
|
prefix, which prints the span along with the result. Moving to
|
||||||
|
a more compact example format:
|
||||||
|
```
|
||||||
|
~tasfyn-partyv:dojo> ? 42
|
||||||
|
@ud
|
||||||
|
42
|
||||||
|
~tasfyn-partyv:dojo> ? 0x2a
|
||||||
|
@ux
|
||||||
|
0x2a
|
||||||
|
```
|
||||||
|
`@ud` and `@ux` stand for "unsigned decimal" and "unsigned hex,"
|
||||||
|
obviously.
|
||||||
|
|
||||||
|
> What is this span syntax? We only derive spans through
|
||||||
|
inference. So there's no parsing grammar for a span. We have to
|
||||||
|
be able to print spans, if only for debugging and diagnostics,
|
||||||
|
but the syntax is output-only. As in this case, it often looks
|
||||||
|
like the `mold` syntax, but the two are at opposite ends of the
|
||||||
|
type food chain.
|
||||||
|
|
||||||
|
## Looking at spans, part 2
|
||||||
|
|
||||||
|
Good style in Hoon is concrete style. When a Hoon programmer
|
||||||
|
defines an abstract semantic value in terms of a noun, we rarely
|
||||||
|
put a conceptual layer of abstraction between value and noun. We
|
||||||
|
think of the semantic value as an interpretation of the
|
||||||
|
concrete noun, and often we just think of the noun.
|
||||||
|
|
||||||
|
With the `?` command, we *do* use an abstract layer, by printing
|
||||||
|
our span noun in a custom syntax. But we can also look at the
|
||||||
|
span noun directly, with the `??` command.
|
||||||
|
|
||||||
|
```
|
||||||
|
~tasfyn-partyv:dojo> ?? 42
|
||||||
|
[%atom %ud]
|
||||||
|
42
|
||||||
|
~tasfyn-partyv:dojo> ?? [42 0x2a]
|
||||||
|
[%cell [%atom %ud] [%atom %ux]]
|
||||||
|
[42 0x2a]
|
||||||
|
```
|
||||||
|
What is this `%atom` syntax? Is it a real noun? Can anyone
|
||||||
|
make one?
|
||||||
|
```
|
||||||
|
~tasfyn-partyv:dojo> %atom
|
||||||
|
%atom
|
||||||
|
~tasfyn-partyv:dojo> %foo
|
||||||
|
%foo
|
||||||
|
~tasfyn-partyv:dojo> [%foo %bar]
|
||||||
|
[%foo %bar]
|
||||||
|
```
|
||||||
|
What's the span of one of these symbols?
|
||||||
|
```
|
||||||
|
~tasfyn-partyv:dojo> ? %foo
|
||||||
|
%foo
|
||||||
|
%foo
|
||||||
|
~tasfyn-partyv:dojo> ?? %foo
|
||||||
|
[%cube 7.303.014 [%atom %tas]]
|
||||||
|
%foo
|
||||||
|
```
|
||||||
|
This takes a little bit of explaining. `7.303.014` is just the
|
||||||
|
Urbit (and German) way of writing the English number `7,303,014`,
|
||||||
|
or the Urbit hex number `0x6f.6f66`, or the string "foo" as an
|
||||||
|
unsigned integer with least-significant byte first.
|
||||||
|
|
||||||
|
A `%cube` span is a constant -- a set of one noun, the atom
|
||||||
|
`7.303.014`. But we still need to know how to print that noun.
|
||||||
|
In this case, it's an `[%atom %tas]`, ie, a text symbol.
|
||||||
|
|
||||||
|
Cubes don't have to be symbols -- in fact, we can take the
|
||||||
|
numbers we've just been using, and make them constants:
|
||||||
|
```
|
||||||
|
~tasfyn-partyv:dojo> %42
|
||||||
|
%42
|
||||||
|
~tasfyn-partyv:dojo> ? %42
|
||||||
|
%42
|
||||||
|
%42
|
||||||
|
~tasfyn-partyv:dojo> ?? %42
|
||||||
|
[%cube 42 [%atom %ud]]
|
||||||
|
%42
|
||||||
|
```
|
||||||
|
|
||||||
|
> Spans are an exception to the concrete style, because we use
|
||||||
|
"manual laziness" to define recursive structures. A recursive
|
||||||
|
span contains Hoon code which is evaluated to apply it. In
|
||||||
|
practice, it often contains the entire Urbit kernel, so you
|
||||||
|
wouldn't want to try to print it in the dojo. If you find
|
||||||
|
`??` taking a weirdly long time, this may have happened; just
|
||||||
|
press ^C.
|
||||||
|
|
||||||
|
## Our first mold
|
||||||
|
|
||||||
|
After seeing a few span examples, are we ready to describe the
|
||||||
|
set of all spans with a Hoon mold? Well, no, but let's try it
|
||||||
|
anyway. Ignore the syntax (which we'll explain later; this is a
|
||||||
|
tutorial, not a reference manual), and you'll get the idea:
|
||||||
|
```
|
||||||
|
++ span
|
||||||
|
$% [%atom p=@tas]
|
||||||
|
[%cell p=span q=span]
|
||||||
|
[%cube p=* q=span]
|
||||||
|
==
|
||||||
|
```
|
||||||
|
This mold is not the entire definition of `span`, just the cases
|
||||||
|
we've seen so far. In English, a valid span is either:
|
||||||
|
|
||||||
|
- a cell with head `%atom`, and tail some symbol.
|
||||||
|
- a cell with head `%cell`, and tail some pair of spans.
|
||||||
|
- a cell with head `%cube`, and tail a noun-span pair.
|
||||||
|
|
||||||
|
The head of a span is essentially the tag in a variant record,
|
||||||
|
a pattern every programming language has. To use the span, we
|
||||||
|
look at the head and then decide what to do with the tail.
|
||||||
|
|
||||||
|
> A conventional naming strategy for simple, self-explaining
|
||||||
|
structures is to name the legs of a tuple `p`, `q`, `r`, `s` and
|
||||||
|
`t`. If you get all the way to `t`, your noun is probably not
|
||||||
|
simple or self-explaining; meaningful names are recommended.
|
||||||
|
|
||||||
|
Believe it or not, at this point we understand nouns completely.
|
||||||
|
We don't understand spans and molds completely, but we get the
|
||||||
|
basics. In the next chapter, we'll see how Hoon expressions
|
||||||
|
(twigs) turn one noun into another.
|
@ -1,229 +0,0 @@
|
|||||||
# Hoon 1: twigs and legs
|
|
||||||
|
|
||||||
In the last chapter we learned how to make nouns. In this
|
|
||||||
chapter we'll start programming a little.
|
|
||||||
|
|
||||||
## Nock for Hoon programmers
|
|
||||||
|
|
||||||
Hoon compiles itself to a pico-interpreter called Nock. 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(subject formula) => product
|
|
||||||
```
|
|
||||||
Your function is the noun `formula`. The input to the function
|
|
||||||
is the noun `subject`. The output is `product`. If something
|
|
||||||
about this seems complicated or even interesting, you may be
|
|
||||||
misunderstanding it.
|
|
||||||
|
|
||||||
## 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, it's not worth the 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 twig is
|
|
||||||
always executed against one input noun, the subject. For any
|
|
||||||
subject noun in `subject-span`, the compiler produces a Nock
|
|
||||||
formula that computes `function-twig` on that subject, and a
|
|
||||||
`product-span` that is the span of the product.
|
|
||||||
|
|
||||||
(Pretty much no other language works this way. In a normal
|
|
||||||
language, your code is executed against a scope, stack, or other
|
|
||||||
variable context, which may not even be a regular user-level
|
|
||||||
value. This change is one of the hardest things to understand
|
|
||||||
about Hoon, mostly because it's hard to stay convinced that
|
|
||||||
subject-oriented programming is as straightforward as it is.)
|
|
||||||
|
|
||||||
## From constants to twigs
|
|
||||||
|
|
||||||
In the last chapter we were entering degenerate twigs like `42`.
|
|
||||||
Obviously this doesn't use the subject at all.
|
|
||||||
|
|
||||||
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]]
|
|
||||||
```
|
|
||||||
We can evaluate twigs against this subject with the Hoon `:`
|
|
||||||
syntax (`a:b` uses the product of `b` as the subject of `a`).
|
|
||||||
```
|
|
||||||
~tasfyn-partyv:dojo> 42:test
|
|
||||||
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]
|
|
||||||
```
|
|
||||||
(If you're wondering why `[6 7]` got printed as `6 7`, remember
|
|
||||||
that `[]` associates to the right.)
|
|
||||||
|
|
||||||
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 is `+n`. Hence:
|
|
||||||
```
|
|
||||||
~tasfyn-partyv:dojo> +1:test
|
|
||||||
[[[8 9] 5] 6 7]
|
|
||||||
```
|
|
||||||
Our example is a sort of Hoon joke, not very funny:
|
|
||||||
```
|
|
||||||
~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
|
|
||||||
```
|
|
||||||
And so on. An instinct for binary tree geometry develops over
|
|
||||||
time as you use the system, rather the way most programmers
|
|
||||||
learn to do binary math.
|
|
||||||
|
|
||||||
## Femur syntax
|
|
||||||
|
|
||||||
A "femur" is an alternative syntax for a tree address. The femur
|
|
||||||
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. The decimal numbers are distracting, whereas the
|
|
||||||
glyph string binds directly to the tree geometry as you learn it.
|
|
||||||
We actually almost never use the decimal tree geometry syntax.
|
|
||||||
|
|
||||||
## Simple faces
|
|
||||||
|
|
||||||
But it would be pretty tough to program in Hoon if explicit
|
|
||||||
geometry 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 @tas]
|
|
||||||
[%cell span span]
|
|
||||||
[%cube * span]
|
|
||||||
[%face @tas 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> 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
|
|
||||||
big 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> =test [[foo=[8 9] 5] foo=[6 7]]
|
|
||||||
~tasfyn-partyv:dojo> ^foo:test
|
|
||||||
[6 7]
|
|
||||||
```
|
|
||||||
`^^foo` will skip two foos, `^^^foo` three, up to `n`.
|
|
||||||
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
|
|
||||||
```
|
|
||||||
It didn't seem to like that. We'll need a nested search:
|
|
||||||
```
|
|
||||||
~tasfyn-partyv:dojo> bar.foo:test
|
|
||||||
7
|
|
||||||
```
|
|
||||||
`bar.foo` here is 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 inside foo."
|
|
||||||
|
|
||||||
Each step in a wing is a `limb`. 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
|
|
||||||
```
|
|
||||||
|
|
||||||
## 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 actually shared) copies of old ones,
|
|
||||||
with changes.
|
|
||||||
|
|
||||||
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 obvious need for the mutant noun to be
|
|
||||||
shaped anything like the old noun. They're different nouns.
|
|
||||||
|
|
||||||
At this point, you have a simplified but basically sound idea of
|
|
||||||
how Hoon builds and manages nouns. Next, it's time to do some
|
|
||||||
programming.
|
|
281
pub/docs/dev/hoon/tutorial/1-twigs.mdy
Normal file
281
pub/docs/dev/hoon/tutorial/1-twigs.mdy
Normal file
@ -0,0 +1,281 @@
|
|||||||
|
---
|
||||||
|
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]]
|
||||||
|
```
|
@ -1,3 +1,9 @@
|
|||||||
|
---
|
||||||
|
title: Hoon 101.2: serious syntax
|
||||||
|
sort: 2
|
||||||
|
hide: true
|
||||||
|
next: false
|
||||||
|
---
|
||||||
# Hoon 2: serious syntax
|
# Hoon 2: serious syntax
|
||||||
|
|
||||||
We've done a bunch of fun stuff on the command line. We know our
|
We've done a bunch of fun stuff on the command line. We know our
|
@ -1,4 +1,10 @@
|
|||||||
# Hoon 3: our first program
|
---
|
||||||
|
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,
|
It's time for us to do some actual programming. In this section,
|
||||||
we'll work through that classic Urbit pons asinorum, decrement.
|
we'll work through that classic Urbit pons asinorum, decrement.
|
@ -1,3 +1,9 @@
|
|||||||
|
---
|
||||||
|
title: Hoon 101.4: functions
|
||||||
|
sort: 4
|
||||||
|
hide: true
|
||||||
|
next: false
|
||||||
|
---
|
||||||
# Hoon 4: toward actual functions
|
# Hoon 4: toward actual functions
|
||||||
|
|
||||||
Okay, we've programmed. We've achieved decrement. We've written
|
Okay, we've programmed. We've achieved decrement. We've written
|
@ -164,6 +164,19 @@ code {
|
|||||||
padding: 0.2rem;
|
padding: 0.2rem;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
blockquote {
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
margin: 0;
|
||||||
|
padding: 1rem;
|
||||||
|
font-style: italic;
|
||||||
|
margin-left: 3rem;
|
||||||
|
}
|
||||||
|
blockquote p {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
blockquote em {
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
ul {
|
ul {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
@ -94,6 +94,19 @@ code
|
|||||||
padding .2rem
|
padding .2rem
|
||||||
display inline-block
|
display inline-block
|
||||||
|
|
||||||
|
blockquote
|
||||||
|
background-color #f5f5f5
|
||||||
|
margin 0
|
||||||
|
padding 1rem
|
||||||
|
font-style italic
|
||||||
|
margin-left 3rem
|
||||||
|
|
||||||
|
blockquote p
|
||||||
|
margin 0
|
||||||
|
|
||||||
|
blockquote em
|
||||||
|
font-style normal
|
||||||
|
|
||||||
ul
|
ul
|
||||||
list-style none
|
list-style none
|
||||||
padding 0
|
padding 0
|
||||||
|
@ -20,7 +20,7 @@ Next = React.createFactory query {
|
|||||||
head:'r'
|
head:'r'
|
||||||
meta:'j'
|
meta:'j'
|
||||||
}, (recl
|
}, (recl
|
||||||
displayName: "Links"
|
displayName: "Next"
|
||||||
render: ->
|
render: ->
|
||||||
curr = @props.kids[@props.curr]
|
curr = @props.kids[@props.curr]
|
||||||
if curr?.meta?.next
|
if curr?.meta?.next
|
||||||
@ -32,9 +32,11 @@ Next = React.createFactory query {
|
|||||||
next = keys[next]
|
next = keys[next]
|
||||||
next = @props.kids[next]
|
next = @props.kids[next]
|
||||||
|
|
||||||
(div {className:"link-next"}, [
|
if next
|
||||||
(a {href:"#{@props.path}/#{next.name}"}, "Next: #{next.meta.title}")
|
return (div {className:"link-next"}, [
|
||||||
])
|
(a {href:"#{@props.path}/#{next.name}"}, "Next: #{next.meta.title}")
|
||||||
|
])
|
||||||
|
return (div {},"")
|
||||||
)
|
)
|
||||||
|
|
||||||
module.exports = query {
|
module.exports = query {
|
||||||
|
@ -35,7 +35,6 @@ module.exports = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},{"../dispatcher/Dispatcher.coffee":14,"../persistence/TreePersistence.coffee":20}],2:[function(require,module,exports){
|
},{"../dispatcher/Dispatcher.coffee":14,"../persistence/TreePersistence.coffee":20}],2:[function(require,module,exports){
|
||||||
var BodyComponent, CLICK, Links, TreeActions, TreeStore, a, clas, div, query, reactify, recl, ref;
|
var BodyComponent, CLICK, Links, TreeActions, TreeStore, a, clas, div, query, reactify, recl, ref;
|
||||||
|
|
||||||
@ -315,7 +314,6 @@ module.exports = query({
|
|||||||
}), div);
|
}), div);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},{"../actions/TreeActions.coffee":1,"../stores/TreeStore.coffee":21,"./Async.coffee":3,"./BodyComponent.coffee":4,"./Reactify.coffee":11,"classnames":16}],3:[function(require,module,exports){
|
},{"../actions/TreeActions.coffee":1,"../stores/TreeStore.coffee":21,"./Async.coffee":3,"./BodyComponent.coffee":4,"./Reactify.coffee":11,"classnames":16}],3:[function(require,module,exports){
|
||||||
var TreeActions, TreeStore, _load, code, div, recl, ref, span;
|
var TreeActions, TreeStore, _load, code, div, recl, ref, span;
|
||||||
|
|
||||||
@ -426,7 +424,6 @@ module.exports = function(queries, Child, load) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},{"../actions/TreeActions.coffee":1,"../stores/TreeStore.coffee":21,"./LoadComponent.coffee":10}],4:[function(require,module,exports){
|
},{"../actions/TreeActions.coffee":1,"../stores/TreeStore.coffee":21,"./LoadComponent.coffee":10}],4:[function(require,module,exports){
|
||||||
var Logo, Next, a, clas, div, img, p, query, reactify, recl, ref;
|
var Logo, Next, a, clas, div, img, p, query, reactify, recl, ref;
|
||||||
|
|
||||||
@ -469,7 +466,7 @@ Next = React.createFactory(query({
|
|||||||
meta: 'j'
|
meta: 'j'
|
||||||
}
|
}
|
||||||
}, recl({
|
}, recl({
|
||||||
displayName: "Links",
|
displayName: "Next",
|
||||||
render: function() {
|
render: function() {
|
||||||
var curr, index, keys, next, ref1;
|
var curr, index, keys, next, ref1;
|
||||||
curr = this.props.kids[this.props.curr];
|
curr = this.props.kids[this.props.curr];
|
||||||
@ -483,15 +480,18 @@ Next = React.createFactory(query({
|
|||||||
}
|
}
|
||||||
next = keys[next];
|
next = keys[next];
|
||||||
next = this.props.kids[next];
|
next = this.props.kids[next];
|
||||||
return div({
|
if (next) {
|
||||||
className: "link-next"
|
return div({
|
||||||
}, [
|
className: "link-next"
|
||||||
a({
|
}, [
|
||||||
href: this.props.path + "/" + next.name
|
a({
|
||||||
}, "Next: " + next.meta.title)
|
href: this.props.path + "/" + next.name
|
||||||
]);
|
}, "Next: " + next.meta.title)
|
||||||
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return div({}, "");
|
||||||
}
|
}
|
||||||
})));
|
})));
|
||||||
|
|
||||||
@ -532,7 +532,6 @@ module.exports = query({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},{"./Async.coffee":3,"./Reactify.coffee":11,"classnames":16}],5:[function(require,module,exports){
|
},{"./Async.coffee":3,"./Reactify.coffee":11,"classnames":16}],5:[function(require,module,exports){
|
||||||
var div, recl, ref, textarea;
|
var div, recl, ref, textarea;
|
||||||
|
|
||||||
@ -556,7 +555,6 @@ module.exports = recl({
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},{}],6:[function(require,module,exports){
|
},{}],6:[function(require,module,exports){
|
||||||
var div, recl;
|
var div, recl;
|
||||||
|
|
||||||
@ -579,7 +577,6 @@ module.exports = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},{"./CodeMirror.coffee":5,"./EmailComponent.coffee":7,"./KidsComponent.coffee":8,"./ListComponent.coffee":9,"./SearchComponent.coffee":12,"./TocComponent.coffee":13}],7:[function(require,module,exports){
|
},{"./CodeMirror.coffee":5,"./EmailComponent.coffee":7,"./KidsComponent.coffee":8,"./ListComponent.coffee":9,"./SearchComponent.coffee":12,"./TocComponent.coffee":13}],7:[function(require,module,exports){
|
||||||
var button, div, input, p, reactify, recl, ref;
|
var button, div, input, p, reactify, recl, ref;
|
||||||
|
|
||||||
@ -660,7 +657,6 @@ module.exports = recl({
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},{"./Reactify.coffee":11}],8:[function(require,module,exports){
|
},{"./Reactify.coffee":11}],8:[function(require,module,exports){
|
||||||
var a, div, hr, li, query, reactify, recl, ref, ul;
|
var a, div, hr, li, query, reactify, recl, ref, ul;
|
||||||
|
|
||||||
@ -732,7 +728,6 @@ module.exports = query({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},{"./Async.coffee":3,"./Reactify.coffee":11}],9:[function(require,module,exports){
|
},{"./Async.coffee":3,"./Reactify.coffee":11}],9:[function(require,module,exports){
|
||||||
var a, clas, div, h1, li, pre, query, reactify, recl, ref, span, ul;
|
var a, clas, div, h1, li, pre, query, reactify, recl, ref, span, ul;
|
||||||
|
|
||||||
@ -888,7 +883,6 @@ module.exports = query({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},{"./Async.coffee":3,"./Reactify.coffee":11,"classnames":16}],10:[function(require,module,exports){
|
},{"./Async.coffee":3,"./Reactify.coffee":11,"classnames":16}],10:[function(require,module,exports){
|
||||||
var div, input, recl, ref, textarea;
|
var div, input, recl, ref, textarea;
|
||||||
|
|
||||||
@ -929,7 +923,6 @@ module.exports = recl({
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},{}],11:[function(require,module,exports){
|
},{}],11:[function(require,module,exports){
|
||||||
var Virtual, div, load, reactify, recl, ref, rele, span, walk;
|
var Virtual, div, load, reactify, recl, ref, rele, span, walk;
|
||||||
|
|
||||||
@ -997,7 +990,6 @@ module.exports = _.extend(reactify, {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},{"./LoadComponent.coffee":10}],12:[function(require,module,exports){
|
},{"./LoadComponent.coffee":10}],12:[function(require,module,exports){
|
||||||
var a, div, input, query, reactify, recl, ref,
|
var a, div, input, query, reactify, recl, ref,
|
||||||
slice = [].slice;
|
slice = [].slice;
|
||||||
@ -1136,7 +1128,6 @@ module.exports = query({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},{"./Async.coffee":3,"./Reactify.coffee":11}],13:[function(require,module,exports){
|
},{"./Async.coffee":3,"./Reactify.coffee":11}],13:[function(require,module,exports){
|
||||||
var div, query, reactify, recl,
|
var div, query, reactify, recl,
|
||||||
slice = [].slice;
|
slice = [].slice;
|
||||||
@ -1265,7 +1256,6 @@ module.exports = query({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},{"./Async.coffee":3,"./Reactify.coffee":11}],14:[function(require,module,exports){
|
},{"./Async.coffee":3,"./Reactify.coffee":11}],14:[function(require,module,exports){
|
||||||
var Dispatcher;
|
var Dispatcher;
|
||||||
|
|
||||||
@ -1287,7 +1277,6 @@ module.exports = _.extend(new Dispatcher(), {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},{"flux":17}],15:[function(require,module,exports){
|
},{"flux":17}],15:[function(require,module,exports){
|
||||||
var rend;
|
var rend;
|
||||||
|
|
||||||
@ -1461,7 +1450,6 @@ $(function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},{"./actions/TreeActions.coffee":1,"./components/AnchorComponent.coffee":2,"./components/BodyComponent.coffee":4,"./components/Components.coffee":6,"./persistence/TreePersistence.coffee":20}],16:[function(require,module,exports){
|
},{"./actions/TreeActions.coffee":1,"./components/AnchorComponent.coffee":2,"./components/BodyComponent.coffee":4,"./components/Components.coffee":6,"./persistence/TreePersistence.coffee":20}],16:[function(require,module,exports){
|
||||||
/*!
|
/*!
|
||||||
Copyright (c) 2015 Jed Watson.
|
Copyright (c) 2015 Jed Watson.
|
||||||
@ -1889,7 +1877,6 @@ module.exports = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},{}],21:[function(require,module,exports){
|
},{}],21:[function(require,module,exports){
|
||||||
var EventEmitter, MessageDispatcher, QUERIES, TreeStore, _curr, _data, _tree, clog;
|
var EventEmitter, MessageDispatcher, QUERIES, TreeStore, _curr, _data, _tree, clog;
|
||||||
|
|
||||||
@ -2115,7 +2102,6 @@ TreeStore.dispatchToken = MessageDispatcher.register(function(payload) {
|
|||||||
module.exports = TreeStore;
|
module.exports = TreeStore;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},{"../dispatcher/Dispatcher.coffee":14,"events":22}],22:[function(require,module,exports){
|
},{"../dispatcher/Dispatcher.coffee":14,"events":22}],22:[function(require,module,exports){
|
||||||
// Copyright Joyent, Inc. and other Node contributors.
|
// Copyright Joyent, Inc. and other Node contributors.
|
||||||
//
|
//
|
||||||
|
Loading…
Reference in New Issue
Block a user