mirror of
https://github.com/ilyakooo0/urbit.git
synced 2024-12-15 10:02:47 +03:00
Merge branch 'test' of github.com:galenwp/urbit into test
This commit is contained in:
commit
505fd03489
@ -21,7 +21,7 @@
|
|||||||
?~ +.arg -
|
?~ +.arg -
|
||||||
(fun.q.q tic.arg)
|
(fun.q.q tic.arg)
|
||||||
%+ sole-lo
|
%+ sole-lo
|
||||||
[%& %helm-begin "your ship: ~"]
|
[%& %helm-begin "your urbit: ~"]
|
||||||
%+ sole-go fed:ag
|
%+ sole-go fed:ag
|
||||||
|= his=@p
|
|= his=@p
|
||||||
%+ sole-lo
|
%+ sole-lo
|
||||||
|
10
pub/doc.md
10
pub/doc.md
@ -1,7 +1,7 @@
|
|||||||
urbit
|
Urbit Manual
|
||||||
=====
|
============
|
||||||
|
|
||||||
is a general-purpose computing stack designed to live in the cloud.
|
Urbit is a general-purpose computing stack designed to live in the cloud.
|
||||||
|
|
||||||
<list dataPreview="true"></list>
|
<list dataPreview="true"></list>
|
||||||
|
|
||||||
@ -11,6 +11,4 @@ is a general-purpose computing stack designed to live in the cloud.
|
|||||||
|
|
||||||
------------------------------------------------------------------------
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
If you're new to the system, take a look at some of the
|
Come join us on `:talk` in the `/urbit-meta` channel to ask questions and get help.
|
||||||
[guides](doc/guide) to get oriented. Come join us on `:talk` in the
|
|
||||||
`/urbit-meta` channel to ask questions and get help.
|
|
||||||
|
@ -1,3 +1,7 @@
|
|||||||
|
---
|
||||||
|
sort: 2
|
||||||
|
---
|
||||||
|
|
||||||
arvo
|
arvo
|
||||||
====
|
====
|
||||||
|
|
@ -1,8 +0,0 @@
|
|||||||
<div class="short">
|
|
||||||
|
|
||||||
`%ives`
|
|
||||||
=======
|
|
||||||
|
|
||||||
Isn't finished yet.
|
|
||||||
|
|
||||||
</div>
|
|
@ -1,5 +0,0 @@
|
|||||||
Ives: Reference
|
|
||||||
===============
|
|
||||||
|
|
||||||
Ives: Commentary
|
|
||||||
================
|
|
@ -1,8 +0,0 @@
|
|||||||
<div class="short">
|
|
||||||
|
|
||||||
`%jael`
|
|
||||||
=======
|
|
||||||
|
|
||||||
Isn't finished yet.
|
|
||||||
|
|
||||||
</div>
|
|
@ -1,5 +0,0 @@
|
|||||||
Jael: Reference
|
|
||||||
===============
|
|
||||||
|
|
||||||
Jael: Commentary
|
|
||||||
================
|
|
@ -1,8 +0,0 @@
|
|||||||
<div class="short">
|
|
||||||
|
|
||||||
`%kahn`
|
|
||||||
=======
|
|
||||||
|
|
||||||
Isn't finished yet.
|
|
||||||
|
|
||||||
</div>
|
|
@ -1,5 +0,0 @@
|
|||||||
Kahn: Reference
|
|
||||||
===============
|
|
||||||
|
|
||||||
Kahn: Commentary
|
|
||||||
================
|
|
@ -1,8 +0,0 @@
|
|||||||
<div class="short">
|
|
||||||
|
|
||||||
`%lunt`
|
|
||||||
=======
|
|
||||||
|
|
||||||
Isn't finished yet.
|
|
||||||
|
|
||||||
</div>
|
|
@ -1,5 +0,0 @@
|
|||||||
Lunt: Reference
|
|
||||||
===============
|
|
||||||
|
|
||||||
Lunt: Commentary
|
|
||||||
================
|
|
@ -1,3 +1,7 @@
|
|||||||
|
---
|
||||||
|
sort: 1
|
||||||
|
---
|
||||||
|
|
||||||
<div class="short">
|
<div class="short">
|
||||||
|
|
||||||
hoon
|
hoon
|
68
pub/doc/hoon/reference/pronunciation.md
Normal file
68
pub/doc/hoon/reference/pronunciation.md
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
Pronunciation
|
||||||
|
=============
|
||||||
|
|
||||||
|
Overview
|
||||||
|
--------
|
||||||
|
|
||||||
|
Hoon is a reserved-word-free
|
||||||
|
language - any text in the program is part of the program.
|
||||||
|
|
||||||
|
We use so many of these ASCII glyphs that we like to be able
|
||||||
|
to read them out loud. A language is meant to be _said_. The
|
||||||
|
squiggles have conventional names, sort of, some of them easy to
|
||||||
|
say, others not so much. So we've renamed them:
|
||||||
|
|
||||||
|
ace space
|
||||||
|
bar |
|
||||||
|
bas \
|
||||||
|
buc $
|
||||||
|
cab _
|
||||||
|
cen %
|
||||||
|
col :
|
||||||
|
com ,
|
||||||
|
doq "
|
||||||
|
dot .
|
||||||
|
fas /
|
||||||
|
gal <
|
||||||
|
gar >
|
||||||
|
hax #
|
||||||
|
hep -
|
||||||
|
kel {
|
||||||
|
ker }
|
||||||
|
ket ^
|
||||||
|
lus +
|
||||||
|
pam &
|
||||||
|
pat @
|
||||||
|
pel (
|
||||||
|
per )
|
||||||
|
sel [
|
||||||
|
sem ;
|
||||||
|
ser ]
|
||||||
|
sig ~
|
||||||
|
soq '
|
||||||
|
tar *
|
||||||
|
tec `
|
||||||
|
tis =
|
||||||
|
wut ?
|
||||||
|
zap !
|
||||||
|
|
||||||
|
Memorizing these names seems like a burden, but it actually makes
|
||||||
|
communicating about hoon code a lot faster and easier. `bartis`
|
||||||
|
sounds a lot better than 'bar equals'.
|
||||||
|
|
||||||
|
To pronounce a rune, concatenate the glyph names, stressing the
|
||||||
|
first syllable and softening the second vowel into a "schwa."
|
||||||
|
Hence, to say `~.`, say "sigdot." To say `|=`, say "bartis."
|
||||||
|
Which has an inevitable tendency to turn into "barts" - a sin
|
||||||
|
to be encouraged.
|
||||||
|
|
||||||
|
There are a few runes with irregular special pronunciations:
|
||||||
|
|
||||||
|
-- hephep phep
|
||||||
|
+- lushep slep
|
||||||
|
++ luslus slus
|
||||||
|
== tistis stet
|
||||||
|
+< lusgal glus
|
||||||
|
+> lusgar gras
|
||||||
|
-< hepgal gelp
|
||||||
|
-> hepgar garp
|
4
pub/doc/hoon/tutorial.md
Normal file
4
pub/doc/hoon/tutorial.md
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
Tutorials
|
||||||
|
=========
|
||||||
|
|
||||||
|
<list dataPreview="true" titlesOnly="true"></list>
|
232
pub/doc/hoon/tutorial/0-nouns.md
Normal file
232
pub/doc/hoon/tutorial/0-nouns.md
Normal file
@ -0,0 +1,232 @@
|
|||||||
|
# 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.
|
229
pub/doc/hoon/tutorial/1-twigs.md
Normal file
229
pub/doc/hoon/tutorial/1-twigs.md
Normal file
@ -0,0 +1,229 @@
|
|||||||
|
# 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.
|
392
pub/doc/hoon/tutorial/2-syntax.md
Normal file
392
pub/doc/hoon/tutorial/2-syntax.md
Normal file
@ -0,0 +1,392 @@
|
|||||||
|
# Hoon 2: serious syntax
|
||||||
|
|
||||||
|
We've done a bunch of fun stuff on the command line. We know our
|
||||||
|
nouns. It's time to actually write some serious code -- in a
|
||||||
|
real source file.
|
||||||
|
|
||||||
|
## Building a simple generator
|
||||||
|
|
||||||
|
In Urbit there's a variety of source file roles, distinguished by
|
||||||
|
the magic paths they're loaded from: `/gen` for generators,
|
||||||
|
`/ape` for appliances, `/fab` for renderers, etc.
|
||||||
|
|
||||||
|
We'll start with a generator, the simplest kind of Urbit program.
|
||||||
|
|
||||||
|
### Create a sandbox desk
|
||||||
|
|
||||||
|
A desk is the Urbit equivalent of a `git` branch. We're just
|
||||||
|
playing around here and don't intend to soil our `%home` desk with
|
||||||
|
test files, so let's make a sandbox:
|
||||||
|
```
|
||||||
|
|merge %sandbox our %home
|
||||||
|
```
|
||||||
|
### Mount the sandbox
|
||||||
|
|
||||||
|
Your Urbit pier is in `~/tasfyn-partyv`, or at least mine is.
|
||||||
|
So we can get our code into Urbit, run the command
|
||||||
|
```
|
||||||
|
~tasfyn-partyv:dojo> |mount /=sandbox=/gen %gen
|
||||||
|
```
|
||||||
|
mounts the `/gen` folder from the `%sandbox` desk in your Unix
|
||||||
|
directory `~/tasfyn-partyv/gen`. The mount is a two-way sync,
|
||||||
|
like your Dropbox. When you edit a Unix file and save, your edit
|
||||||
|
is automatically committed as a change to `%sandbox`.
|
||||||
|
|
||||||
|
### Execute from the sandbox
|
||||||
|
|
||||||
|
The `%sandbox` desk obviously is merged from `%home`, so it
|
||||||
|
contains find all the default facilities you'd expect there.
|
||||||
|
Bear in mind, we didn't set it to auto-update when `%home`
|
||||||
|
is updated (that would be `|sync` instead of `|merge`).
|
||||||
|
|
||||||
|
So we're not roughing it when we set the dojo to load from
|
||||||
|
`%sandbox`:
|
||||||
|
```
|
||||||
|
[switch to %home]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Write your builder
|
||||||
|
|
||||||
|
Let's build the simplest possible kind of generator, a builder.
|
||||||
|
With your favorite Unix text editor (there are Hoon modes for vim
|
||||||
|
and emacs), create the file `~/tasfyn-partyv/gen/test.hoon`.
|
||||||
|
Edit it into this:
|
||||||
|
```
|
||||||
|
:- %say |= * :- %noun
|
||||||
|
[%hello %world]
|
||||||
|
```
|
||||||
|
Get the spaces exactly right, please. Hoon is not in general a
|
||||||
|
whitespace-sensitive language, but the difference between one
|
||||||
|
space and two-or-more matters. And for the moment, think of
|
||||||
|
```
|
||||||
|
:- %say |= * :- %noun
|
||||||
|
```
|
||||||
|
as gibberish boilerplate at the start of a file, like `#include
|
||||||
|
"stdio.h"` at the start of a C program. Any of our old Hoon
|
||||||
|
constants would work in place of `[%hello %world`].
|
||||||
|
|
||||||
|
Now, run your builder:
|
||||||
|
```
|
||||||
|
~tasfyn-partyv:dojo/sandbox> +test
|
||||||
|
[%hello %world]
|
||||||
|
```
|
||||||
|
Obviously this is your first Hoon *program* per se.
|
||||||
|
|
||||||
|
## Hoon syntax 101
|
||||||
|
|
||||||
|
But what's up with this syntax?
|
||||||
|
|
||||||
|
### A syntactic apology
|
||||||
|
|
||||||
|
The relationship between ASCII and human programming languages
|
||||||
|
is like the relationship between the electric guitar and
|
||||||
|
rock-and-roll. If it doesn't have a guitar, it's not rock.
|
||||||
|
Some great rockers play three chords, like Johnny Ramone; some
|
||||||
|
shred it up, like Jimmy Page.
|
||||||
|
|
||||||
|
The two major families of ASCII-shredding languages are Perl and
|
||||||
|
the even more spectacular APL. (Using non-ASCII characters is
|
||||||
|
just a fail, but APL successors like J fixed this.) No one
|
||||||
|
has any right to rag on Larry Wall or Ken Iverson, but Hoon,
|
||||||
|
though it shreds, shreds very differently.
|
||||||
|
|
||||||
|
The philosophical case for a "metalhead" language is threefold.
|
||||||
|
One, human beings are much better at associating meaning with
|
||||||
|
symbols than they think they are. Two, a programming language is
|
||||||
|
a professional tool and not a plastic shovel for three-year-olds.
|
||||||
|
|
||||||
|
And three, the alternative to heavy metal is keywords. When you
|
||||||
|
use a keyword language, not only are you forcing the programmer
|
||||||
|
to tiptoe around a ridiculous maze of restricted words used and
|
||||||
|
reserved, you're expressing your program through two translation
|
||||||
|
steps: symbol->English and English->computation. When you shred,
|
||||||
|
you are going direct: symbol->computation. Especially in a pure
|
||||||
|
language, this creates a sense of "seeing the function" which no
|
||||||
|
keyword language can quite duplicate.
|
||||||
|
|
||||||
|
But any metalhead language you don't yet know is line noise.
|
||||||
|
Let's get you up to speed as fast as possible.
|
||||||
|
|
||||||
|
### A glyphic bestiary
|
||||||
|
|
||||||
|
A programming language needs to be not just read but said. But
|
||||||
|
no one wants to say "ampersand." Therefore, we've taken the
|
||||||
|
liberty of assigning three-letter names to all ASCII glyphs.
|
||||||
|
|
||||||
|
Some of these bindings are obvious and some aren't. You'll be
|
||||||
|
genuinely surprised at how easy they are to remember:
|
||||||
|
```
|
||||||
|
ace [1 space] dot . pan ]
|
||||||
|
bar | fas / pel )
|
||||||
|
bis \ gap [>1 space, nl] pid }
|
||||||
|
buc $ hax # ran >
|
||||||
|
cab _ ket ^ rep '
|
||||||
|
cen % lep ( sac ;
|
||||||
|
col : lit < tar *
|
||||||
|
com , lus + tec `
|
||||||
|
das - mat @ tis =
|
||||||
|
den " med & wut ?
|
||||||
|
dip { nap [ zap !
|
||||||
|
```
|
||||||
|
It's fun to confuse people by using these outside Urbit. A few
|
||||||
|
digraphs also have irregular sounds:
|
||||||
|
```
|
||||||
|
== stet
|
||||||
|
-- shed
|
||||||
|
++ slus
|
||||||
|
-> dart
|
||||||
|
-< dusk
|
||||||
|
+> lark
|
||||||
|
+< lush
|
||||||
|
```
|
||||||
|
|
||||||
|
### The shape of a twig
|
||||||
|
|
||||||
|
A twig, of course, is a noun. As usual, the easiest way to
|
||||||
|
explain both the syntax that compiles into that noun, and the
|
||||||
|
semantic meaning of the noun, is the noun's physical structure.
|
||||||
|
|
||||||
|
#### Autocons
|
||||||
|
|
||||||
|
A twig is always a cell, and any cell of twigs is a twig
|
||||||
|
producing a cell. As an homage to Lisp, we call this
|
||||||
|
"autocons." Where you'd write `(cons a b)` in Lisp, you write
|
||||||
|
`[a b]` in Hoon, and the shape of the twig follows.
|
||||||
|
|
||||||
|
The `???` prefix prints a twig as a noun instead of running it.
|
||||||
|
Let's see autocons in action:
|
||||||
|
```
|
||||||
|
~tasfyn-partyv:dojo/sandbox> ??? 42
|
||||||
|
[%dtzy %ud 42]
|
||||||
|
~tasfyn-partyv:dojo/sandbox> ??? 0x2a
|
||||||
|
[%dtzy %ux 42]
|
||||||
|
~tasfyn-partyv:dojo/sandbox> ??? [42 0xa]
|
||||||
|
[[%dtzy %ud 42] %dtzy %ux 42]
|
||||||
|
```
|
||||||
|
(As always, it may confuse *you* that this is the same noun as
|
||||||
|
`[[%dtzy %ud 42] [%dtzy %ux 42]]`, but it doesn't confuse Hoon.)
|
||||||
|
|
||||||
|
#### The stem-bulb pattern
|
||||||
|
|
||||||
|
If the head of your twig is a cell, it's an autocons. If the
|
||||||
|
head is an atom, it's an unpronounceable four-letter symbol like
|
||||||
|
the `%dtzy` above.
|
||||||
|
|
||||||
|
This is the same pattern as we see in the `span` mold -- a
|
||||||
|
variant record, essentially, in nouns. The head of one of these
|
||||||
|
cells is called the "stem." The tail is the "bulb." The shape
|
||||||
|
of the bulb is totally dependent on the value of the stem.
|
||||||
|
|
||||||
|
#### Runes and stems
|
||||||
|
|
||||||
|
A "rune" (a word intentionally chosen to annoy Go programmers) is
|
||||||
|
a digraph - a sequence of two ASCII glyphs. If you know C, you
|
||||||
|
know digraphs like `->` and `?:` and are used to reading them as
|
||||||
|
single characters.
|
||||||
|
|
||||||
|
In Hoon you can *say* them as words: "dasran" and "wattis"
|
||||||
|
respectively. In a metalhead language, if we had to say
|
||||||
|
"minus greater-than" and "question-colon", we'd just die.
|
||||||
|
|
||||||
|
Most twig stems are made from runes, by concatenating the glyph
|
||||||
|
names and removing the vowels. For example, the rune `=+`,
|
||||||
|
pronounced "tislus," becomes the stem `%tsls`. (Note that in
|
||||||
|
many noun implementations, this is a 31-bit direct value.)
|
||||||
|
|
||||||
|
(Some stems (like `%dtzy`) are not runes, simply because they
|
||||||
|
don't have regular-form syntax and don't need to use precious
|
||||||
|
ASCII real estate. They are otherwise no different.)
|
||||||
|
|
||||||
|
An important point to note about runes: they're organized. The
|
||||||
|
first glyph in the rune defines a category. For instance, runes
|
||||||
|
starting with `.` compute intrinsics; runes starting with `|`
|
||||||
|
produce cores; etc.
|
||||||
|
|
||||||
|
Another important point about runes: they come in two flavors,
|
||||||
|
"natural" (stems interpreted directly by the compiler) and
|
||||||
|
"synthetic" (macros, essentially).
|
||||||
|
|
||||||
|
(Language food fight warning: one advantage of Hoon over Lisp is
|
||||||
|
that all Hoon macros are inherently hygienic. Another advantage
|
||||||
|
is that Hoon has no (user-level) macros. In Hoon terms, nobody
|
||||||
|
gets to invent their own runes. A DSL is always and everywhere
|
||||||
|
a write-only language. Hoon shreds its ASCII pretty hard, but
|
||||||
|
the same squiggles mean the same things in everyone's code.)
|
||||||
|
|
||||||
|
#### Wide and tall regular forms
|
||||||
|
|
||||||
|
A good rune example is the simple rune `=+`, pronounced "tislus",
|
||||||
|
which becomes the stem `%tsls`. A `%tsls` twig has the shape
|
||||||
|
`[%tsls twig twig]`.
|
||||||
|
|
||||||
|
The very elegance of functional languages creates a visual
|
||||||
|
problem that imperative languages lack. An imperative language
|
||||||
|
has distinct statements (with side effects) and (usually pure)
|
||||||
|
expressions; it's natural that in most well-formatted code,
|
||||||
|
statements flow vertically down the screen, and expressions grow
|
||||||
|
horizontally across this. This interplay creates a natural and
|
||||||
|
relaxing shape on your screen.
|
||||||
|
|
||||||
|
In a functional language, there's no difference. The trivial
|
||||||
|
functional syntax is Lisp's, which has two major problems. One:
|
||||||
|
piles of expression terminators build up at the bottom of complex
|
||||||
|
functions. Two: the natural shape of code is diagonal. The more
|
||||||
|
complex a function, the more it wants to besiege the right
|
||||||
|
margin. The children of a node have to start to the right of its
|
||||||
|
parent, so the right margin bounds the tree depth.
|
||||||
|
|
||||||
|
Hoon does not completely solve these problems, but alleviates
|
||||||
|
them. In Hoon, there are actually two regular syntax forms for
|
||||||
|
most twig cases: "tall" and "wide" form. Tall twigs can contain
|
||||||
|
wide twigs, but not vice versa, so the visual shape of a program
|
||||||
|
is very like that of a statements-and-expressions language.
|
||||||
|
|
||||||
|
Also, in tall mode, most runes don't need terminators. Take
|
||||||
|
`=+`, for example. Since the parser knows to expect exactly
|
||||||
|
two twigs after the `=+` rune, it doesn't need any extra syntax
|
||||||
|
to tell it that it's done.
|
||||||
|
|
||||||
|
Let's try a wide `=+` in the dojo:
|
||||||
|
```
|
||||||
|
~tasfyn-partyv:dojo/sandbox> =+(planet=%world [%hello planet])
|
||||||
|
[%hello %world]
|
||||||
|
```
|
||||||
|
(`=+` seems to be some sort of variable declaration? Let's not
|
||||||
|
worry about it right now. We're on syntax.)
|
||||||
|
|
||||||
|
The wide syntax for a `=+` twig, or any binary rune: `(`, the
|
||||||
|
first subtwig, one space, the second subtwig, and `)`). To read
|
||||||
|
this twig out loud, you'd say:
|
||||||
|
```
|
||||||
|
tislus lap planet is cen world ace nep cen hello ace planet pen
|
||||||
|
pal
|
||||||
|
```
|
||||||
|
("tis" not in a rune gets contracted to "is".)
|
||||||
|
|
||||||
|
Let's try a tall `=+` in `test.hoon`:
|
||||||
|
```
|
||||||
|
:- %say |= * :- %noun
|
||||||
|
=+ planet=%world
|
||||||
|
[%hello planet]
|
||||||
|
```
|
||||||
|
The tall syntax for a `=+` twig, or any binary rune: the rune, at
|
||||||
|
least two spaces or one newline, the first subtwig, at least two
|
||||||
|
spaces or one newline, the second subtwig. Again, tall subtwigs
|
||||||
|
can be tall or wide; wide subtwigs have to be wide.
|
||||||
|
|
||||||
|
(Note that our boilerplate line is a bunch of tall runes on one
|
||||||
|
line, with two-space gaps. This is unusual but quite legal, and
|
||||||
|
not to be confused with the actual wide form.)
|
||||||
|
|
||||||
|
To read this twig out loud, you'd say:
|
||||||
|
```
|
||||||
|
tislus gap planet is cen world gap nep cen hello ace planet pen
|
||||||
|
```
|
||||||
|
#### Layout conventions
|
||||||
|
|
||||||
|
Should you use wide twigs or tall twigs? When? How? What
|
||||||
|
should your code look like? You're the artist. Except for the
|
||||||
|
difference between one space (`ace`) and more space (`gap`), the
|
||||||
|
parser doesn't care how you format your code. Hoon is not Go --
|
||||||
|
there are no fixed rules for doing it right.
|
||||||
|
|
||||||
|
However, the universal convention is to keep lines under 80
|
||||||
|
characters. Also, hard tab characters are illegal. And when in
|
||||||
|
doubt, make your code look like the kernel code.
|
||||||
|
|
||||||
|
##### Backstep indentation
|
||||||
|
|
||||||
|
Note that the "variable declaration" concept of `=+` (which is no
|
||||||
|
more a variable declaration than a Tasmanian tiger is a tiger)
|
||||||
|
works perfectly here. Because `[%hello planet]` -- despite being
|
||||||
|
a subtree of the the `=+` twig -- is at the same indent level.
|
||||||
|
So our code flows down the screen, not down and to the right, and
|
||||||
|
of course there are no superfluous terminators. It looks good,
|
||||||
|
and creates fewer hard-to-find syntax errors than you'd think.
|
||||||
|
|
||||||
|
This is called "backstep" indentation. Another example, using a
|
||||||
|
ternary rune that has a strange resemblance to C:
|
||||||
|
```
|
||||||
|
:- %say |= * :- %noun
|
||||||
|
=+ planet=%world
|
||||||
|
?: =(%world planet)
|
||||||
|
[%hello planet]
|
||||||
|
[%goodbye planet]
|
||||||
|
```
|
||||||
|
It's not always the case when backstepping that the largest
|
||||||
|
subtwig is at the bottom and loses no margin, but it often is.
|
||||||
|
And not all runes have tuple structure; some are n-ary, and use
|
||||||
|
the `==` terminator (again, pronounced "stet"):
|
||||||
|
```
|
||||||
|
:- %say |= * :- %noun
|
||||||
|
=+ planet=%world
|
||||||
|
?+ planet
|
||||||
|
[%unknown planet]
|
||||||
|
%world [%hello planet]
|
||||||
|
%ocean [%goodbye planet]
|
||||||
|
==
|
||||||
|
```
|
||||||
|
So we occasionally lose right-margin as we descend a deep twig.
|
||||||
|
But we can keep this lossage low with good layout design. The
|
||||||
|
goal is to keep the heavy twigs on the right, and Hoon tries as
|
||||||
|
hard as possible to help you with this.
|
||||||
|
|
||||||
|
For instance, `=+` ("tislus") is a binary rune: `=+(a b)`. In
|
||||||
|
most cases of `=+` the heavy twig is `b`, but sometimes it's `a`.
|
||||||
|
So we can use its friend the `=-` rune ("tisdas") to get the same
|
||||||
|
semantics with the right shape: `=-(b a)`.
|
||||||
|
|
||||||
|
#### Irregular forms
|
||||||
|
|
||||||
|
There are more regular forms than we've shown above, but not a
|
||||||
|
lot more. Hoon would be quite easy to learn if it was only its
|
||||||
|
regular forms. It wouldn't be as easy to read or use, though.
|
||||||
|
The learning curve is important, but not all-important.
|
||||||
|
|
||||||
|
Some stems (like the `%dtzy` constants above) obviously don't and
|
||||||
|
can't have any kind of regular form (which is why `%dtzy` is not
|
||||||
|
a real digraph rune). Many of the true runes have only regular
|
||||||
|
forms. But some have irregular forms. Irregular forms are
|
||||||
|
always wide, but there is no other constraint on their syntax.
|
||||||
|
|
||||||
|
We've already encountered one of the irregular forms: `foo=42`
|
||||||
|
from the last chapter, and `planet=%world` here. Let's unpack
|
||||||
|
this twig:
|
||||||
|
```
|
||||||
|
~tasfyn-partyv:dojo/sandbox> ?? %world
|
||||||
|
[%cube 431.316.168.567 %atom %tas]
|
||||||
|
%world
|
||||||
|
|
||||||
|
~tasfyn-partyv:dojo/sandbox> ??? %world
|
||||||
|
[%dtzz %tas 431.316.168.567]
|
||||||
|
```
|
||||||
|
Clearly, `%dtzz` is one of our non-regulars. But we can wrap it
|
||||||
|
with our irregular form:
|
||||||
|
```
|
||||||
|
~tasfyn-partyv:dojo/sandbox> ?? planet=%world
|
||||||
|
[%face %planet [%cube 431.316.168.567 %atom %tas]]
|
||||||
|
planet=%world
|
||||||
|
|
||||||
|
~tasfyn-partyv:dojo/sandbox> ??? planet=%world
|
||||||
|
[%ktts %planet %dtzz %tas 431.316.168.567]
|
||||||
|
```
|
||||||
|
Since `%ktts` is "kettis", ie, `^=`, this has to be the irregular
|
||||||
|
form of
|
||||||
|
```
|
||||||
|
~tasfyn-partyv:dojo/sandbox> ^=(planet %world)
|
||||||
|
planet=world
|
||||||
|
```
|
||||||
|
So if we wrote our example without this irregular form, it'd be
|
||||||
|
```
|
||||||
|
:- %say |= * :- %noun
|
||||||
|
=+ ^=(planet %world)
|
||||||
|
[%hello planet]
|
||||||
|
```
|
||||||
|
Or with a gratuitous use of tall form:
|
||||||
|
```
|
||||||
|
:- %say |= * :- %noun
|
||||||
|
=+ ^= planet %world
|
||||||
|
[%hello planet]
|
||||||
|
```
|
||||||
|
Now you know how to read Hoon! For fun, try to pronounce more of
|
||||||
|
the code on this page. Please don't laugh too hard at yourself.
|
316
pub/doc/hoon/tutorial/3-program.md
Normal file
316
pub/doc/hoon/tutorial/3-program.md
Normal file
@ -0,0 +1,316 @@
|
|||||||
|
# Hoon 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 ("tisran"), 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 `=<` ("tislit"). 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 (`&`, "rob") 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?
|
261
pub/doc/hoon/tutorial/4-functions.md
Normal file
261
pub/doc/hoon/tutorial/4-functions.md
Normal file
@ -0,0 +1,261 @@
|
|||||||
|
# Hoon 4: toward actual functions
|
||||||
|
|
||||||
|
Okay, we've programmed. We've achieved decrement. We've written
|
||||||
|
what is in some sense a loop. What next?
|
||||||
|
|
||||||
|
Well... we're still feeling vaguely disappointed. Because we're
|
||||||
|
supposed to be doing *functional programming*. And we haven't
|
||||||
|
yet written any *functions*.
|
||||||
|
|
||||||
|
After all, in Hoon we don't really write a command-line utility
|
||||||
|
to decrement `42`. We write `(dec 42)`. You probably realize
|
||||||
|
that on the inside, this is not the same thing as a function in a
|
||||||
|
normal functional language. The Tasmanian tiger is not a tiger.
|
||||||
|
On the other hand, it certainly *looks* like a function call.
|
||||||
|
|
||||||
|
So how do we write the function?
|
||||||
|
|
||||||
|
In this chapter, we'll modify `+test` to extend the subject so
|
||||||
|
that we can write our result as `(dec arg)`. Or rather, `(duck
|
||||||
|
arg)`, because we want to get out of training wheels and stop
|
||||||
|
clearing the subject soon.
|
||||||
|
|
||||||
|
## Form of the solution
|
||||||
|
|
||||||
|
```
|
||||||
|
=< :- %say |= [* [[arg=@ud ~] ~]] :- %noun
|
||||||
|
(duck arg)
|
||||||
|
!! :: some interesting core
|
||||||
|
```
|
||||||
|
`!!`, or "zapzap" or `[%zpzp ~]`, can go anywhere a twig can and
|
||||||
|
always crashes. Because its span is the empty set (`%void`), it
|
||||||
|
doesn't cause type inference problems.
|
||||||
|
|
||||||
|
In place of the `!!`, we'll put a core, effectively a library,
|
||||||
|
that provides our new, improved decrement function `duck`. We'll
|
||||||
|
then call it with the irregular form, `(duck arg)`, which looks
|
||||||
|
like a function call but is in fact some mysterious macro.
|
||||||
|
|
||||||
|
## Some interesting core
|
||||||
|
|
||||||
|
Translated into imperative programming, what we did in chapter 3
|
||||||
|
was more like computing a function of a global variable. Now,
|
||||||
|
we have to actually pass an argument to a function.
|
||||||
|
|
||||||
|
Here's our first try:
|
||||||
|
```
|
||||||
|
=< :- %say |= [* [[arg=@ud ~] ~]] :- %noun
|
||||||
|
=+ gat=duck
|
||||||
|
=<(run gat(sam arg))
|
||||||
|
=> ~
|
||||||
|
|%
|
||||||
|
++ duck
|
||||||
|
=+ sam=0
|
||||||
|
=+ pre=0
|
||||||
|
|%
|
||||||
|
++ run
|
||||||
|
?: =(sam +(pre))
|
||||||
|
pre
|
||||||
|
run(pre +(pre))
|
||||||
|
--
|
||||||
|
--
|
||||||
|
|
||||||
|
~tasfyn-partyv:dojo/sandbox> +test 42
|
||||||
|
41
|
||||||
|
```
|
||||||
|
We step back and contemplate our handiwork. Is it good? Well...
|
||||||
|
it works. Reading programs written without syntactic sugar is
|
||||||
|
about as fun as eating raw chocolate nibs.
|
||||||
|
|
||||||
|
What did we do? In the `duck` arm (we often write `++duck`, for
|
||||||
|
obvious reasons) we produce a core whose payload is `[pre=0 num=0
|
||||||
|
~]`, and whose battery contains `++run`.
|
||||||
|
|
||||||
|
In the result twig, we first use `++duck` to extend our subject
|
||||||
|
with a core named `gat`. We then use `run` on that gate. Why do
|
||||||
|
we need this `gat`? Why can't we just write `=<(run duck(sam
|
||||||
|
arg))`?
|
||||||
|
|
||||||
|
Because the arm is computed *after* the mutation. But here we
|
||||||
|
need the mutated *result* of `++duck`. Instead, what this code
|
||||||
|
is doing is trying to mutate `sam` within the core that contains
|
||||||
|
`++duck`. Where it doesn't exist, so your code won't compile.
|
||||||
|
|
||||||
|
And note that with `=<`, we've placed our library structurally
|
||||||
|
between the original subject and the program we're writing,
|
||||||
|
but lexically at the bottom with zero left margin. We also
|
||||||
|
clear the subject to keep things simple.
|
||||||
|
|
||||||
|
## A more regular structure
|
||||||
|
|
||||||
|
It actually gets worse. To make this code look simpler, we need
|
||||||
|
to make it more complex. While "function calls" actually fit
|
||||||
|
quite well into the Hoon architecture, they're also a nontrivial
|
||||||
|
synthetic construction. We'll build the desugared form the hard
|
||||||
|
way, then show you where we put the sugar in.
|
||||||
|
|
||||||
|
The desugared canonical decrement:
|
||||||
|
```
|
||||||
|
=< :- %say |= [* [[arg=@ud ~] ~]] :- %noun
|
||||||
|
=+ gat=duck
|
||||||
|
=<(run gat(sam arg))
|
||||||
|
=> ~
|
||||||
|
|%
|
||||||
|
++ duck
|
||||||
|
=+ sam=0
|
||||||
|
|%
|
||||||
|
++ run
|
||||||
|
=+ pre=0
|
||||||
|
=< loop
|
||||||
|
|%
|
||||||
|
++ loop
|
||||||
|
?: =(sam +(pre))
|
||||||
|
pre
|
||||||
|
loop(pre +(pre))
|
||||||
|
--
|
||||||
|
--
|
||||||
|
--
|
||||||
|
|
||||||
|
~tasfyn-partyv:dojo/sandbox> +test 42
|
||||||
|
41
|
||||||
|
```
|
||||||
|
Yuck. Okay, let's fix this.
|
||||||
|
|
||||||
|
## Art of the loop
|
||||||
|
|
||||||
|
First, look at our little `++loop`. It works just like our old
|
||||||
|
`++run` loop. We notice that there's actually something nice
|
||||||
|
about it: we don't use the symbol `loop` anywhere outside these 7
|
||||||
|
lines of code. It's not exported at all.
|
||||||
|
|
||||||
|
Actually, the symbol `loop` name is useless and redundant.
|
||||||
|
Making up names is one of the hard problems in computer science,
|
||||||
|
so why solve it? For just this reason, Hoon has an *empty name*,
|
||||||
|
which as a constant is a zero-length symbol (`%$` instead of
|
||||||
|
`%foo`), and as a limb is the `buc` symbol (`$`). With `$`,
|
||||||
|
our loop becomes:
|
||||||
|
```
|
||||||
|
=< $
|
||||||
|
|%
|
||||||
|
++ $
|
||||||
|
?: =(sam +(pre))
|
||||||
|
pre
|
||||||
|
$(sam +(run))
|
||||||
|
--
|
||||||
|
```
|
||||||
|
This may not seem like a huge improvement. It's not. But it's
|
||||||
|
exactly equivalent to the synthetic rune `|-`, "bardas":
|
||||||
|
```
|
||||||
|
|- ?: =(sam +(pre))
|
||||||
|
pre
|
||||||
|
$(pre +(pre))
|
||||||
|
```
|
||||||
|
This is obviously the canonical Hoon loop. It leaves us with
|
||||||
|
```
|
||||||
|
=< :- %say |= [* [[arg=@ud ~] ~]] :- %noun
|
||||||
|
=+ gat=duck
|
||||||
|
=<(run gat(sam arg))
|
||||||
|
=> ~
|
||||||
|
|%
|
||||||
|
++ duck
|
||||||
|
=+ sam=0
|
||||||
|
|%
|
||||||
|
++ run
|
||||||
|
=+ pre=0
|
||||||
|
|- ?: =(sam +(pre))
|
||||||
|
pre
|
||||||
|
$(pre +(pre))
|
||||||
|
--
|
||||||
|
--
|
||||||
|
|
||||||
|
~tasfyn-partyv:dojo/sandbox> +test 42
|
||||||
|
41
|
||||||
|
```
|
||||||
|
|
||||||
|
## Is this a lambda?
|
||||||
|
|
||||||
|
Could we use `$` for `++run`? It certainly sounds like the same
|
||||||
|
kind of thing as `++loop` -- just a word we invented to mean "do
|
||||||
|
it." Should the programmer have to invent these kinds of words?
|
||||||
|
```
|
||||||
|
=< :- %say |= [* [[arg=@ud ~] ~]] :- %noun
|
||||||
|
=+ gat=duck
|
||||||
|
=<($ gat(sam arg))
|
||||||
|
=> ~
|
||||||
|
|%
|
||||||
|
++ duck
|
||||||
|
=| sam=@ud
|
||||||
|
|%
|
||||||
|
=+ pre=0
|
||||||
|
++ $
|
||||||
|
|- ?: =(sam +(pre))
|
||||||
|
pre
|
||||||
|
$(pre +(pre))
|
||||||
|
--
|
||||||
|
--
|
||||||
|
|
||||||
|
~tasfyn-partyv:dojo/sandbox> +test 42
|
||||||
|
41
|
||||||
|
```
|
||||||
|
(Besides `run` to `$`, we changed `=+ sam=0` to `=| sam=@ud`.
|
||||||
|
Let's just remember that there's some magic here. We'll come
|
||||||
|
back and explain it later.)
|
||||||
|
|
||||||
|
This is still kind of ugly -- but it's exactly equivalent to
|
||||||
|
```
|
||||||
|
=< :- %say |= [* [[arg=@ud ~] ~]] :- %noun
|
||||||
|
=+ gat=duck
|
||||||
|
=<($ gat(sam arg))
|
||||||
|
=> ~
|
||||||
|
|%
|
||||||
|
++ duck
|
||||||
|
|= sam=@ud
|
||||||
|
=+ pre=0
|
||||||
|
|- ?: =(sam +(pre))
|
||||||
|
pre
|
||||||
|
$(pre +(pre))
|
||||||
|
--
|
||||||
|
|
||||||
|
~tasfyn-partyv:dojo/sandbox> +test 42
|
||||||
|
41
|
||||||
|
```
|
||||||
|
Doesn't that look like a function? Indeed, we're done with
|
||||||
|
`++duck` -- that's what a Hoon decrement should look like.
|
||||||
|
If you squint a little, `|=` ("bartis") might even be a strange,
|
||||||
|
deformed lambda rune.
|
||||||
|
|
||||||
|
Since it's doing something simple, we might well even compress
|
||||||
|
the whole body of the function into one wide-form line:
|
||||||
|
```
|
||||||
|
=+(pre=0 |-(?:(=(sam +(pre)) pre $(pre +(pre)))))
|
||||||
|
```
|
||||||
|
(According, of course, to taste -- this is a bit tight for some.)
|
||||||
|
|
||||||
|
## Gates and how to call them
|
||||||
|
|
||||||
|
Our call site remains a disaster, though. We'll need moar sugar.
|
||||||
|
|
||||||
|
But first, let's look at this lambda-thing we've made. What is
|
||||||
|
the noun produced by `++duck`? Our term for it is a "gate," but
|
||||||
|
nobody will hate you for saying "function." And while we "slam"
|
||||||
|
our gates, you can feel free to just "call" them.
|
||||||
|
|
||||||
|
A gate is a core, of course, but a special kind of core. All
|
||||||
|
cores are shaped like `[battery payload]`. A gate is shaped like
|
||||||
|
`[formula [sample context]]`. A gate has one arm, `$`, so its
|
||||||
|
battery is just a formula. To slam a gate, you replace its
|
||||||
|
sample (`+6` or `+<`, "luslit" or "lust") with your own noun,
|
||||||
|
and apply the formula to the mutated gate.
|
||||||
|
|
||||||
|
As we explained earlier, `duck(sam arg)` is not the right way to
|
||||||
|
mutate the gate we make with `duck`, because it's actually
|
||||||
|
trying to mutate the core we used to make `duck`. But there has
|
||||||
|
to be some sugar to do this, and there is: `%*`, "centar". We
|
||||||
|
can replace our call site with `%*($ duck sam arg)`.
|
||||||
|
|
||||||
|
This is also not quite orthodox, because the whole point of a
|
||||||
|
gate is the canonical shape that defines a calling convention.
|
||||||
|
We can and should say: `%*($ duck +< arg)`.
|
||||||
|
|
||||||
|
Unsurprisingly, this in turn is `%-(duck arg)` in regular form,
|
||||||
|
or `(duck arg)`
|
@ -1,9 +1,13 @@
|
|||||||
|
---
|
||||||
|
sort: 3
|
||||||
|
---
|
||||||
|
|
||||||
<div class="short">
|
<div class="short">
|
||||||
|
|
||||||
References
|
Interpreter
|
||||||
==========
|
==========
|
||||||
|
|
||||||
These references cover terminology and our interpreter, `vere`.
|
The urbit interpreter and C code.
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -1,3 +1,7 @@
|
|||||||
|
---
|
||||||
|
sort: 0
|
||||||
|
---
|
||||||
|
|
||||||
<div class="short">
|
<div class="short">
|
||||||
|
|
||||||
nock
|
nock
|
@ -1,14 +0,0 @@
|
|||||||
<div class="short">
|
|
||||||
|
|
||||||
talk
|
|
||||||
====
|
|
||||||
|
|
||||||
talk is our messaging application.
|
|
||||||
|
|
||||||
talk is in dire need of better documentation
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
|
|
||||||
This document should include <a href="./talk/help">the command reference</a> somehow.
|
|
@ -1,24 +0,0 @@
|
|||||||
Besides `;help`, there are four main `talk` commands:
|
|
||||||
|
|
||||||
;join ~urbit-name/channel
|
|
||||||
|
|
||||||
`;join` subscribes your main feed to a remote channel.
|
|
||||||
|
|
||||||
;create channel %name 'description'
|
|
||||||
|
|
||||||
`;create` creates a channel on your urbit.
|
|
||||||
|
|
||||||
;<number>
|
|
||||||
|
|
||||||
`;<number>` activates a previous message number, like a URL that got
|
|
||||||
clipped.
|
|
||||||
|
|
||||||
;<target>
|
|
||||||
|
|
||||||
`;<target>` sets the target for your messages, such as `;~urbit-name`
|
|
||||||
for a private message, or `;/channel`
|
|
||||||
|
|
||||||
;
|
|
||||||
|
|
||||||
By itself is "autotarget", and maintains the audience of the last message
|
|
||||||
heard.
|
|
10
pub/doc/tools.mdy
Normal file
10
pub/doc/tools.mdy
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
---
|
||||||
|
sort: 4
|
||||||
|
---
|
||||||
|
|
||||||
|
tools
|
||||||
|
====
|
||||||
|
|
||||||
|
User-level tools and utilities.
|
||||||
|
|
||||||
|
<list></list>
|
207
pub/doc/tools/clay.md
Normal file
207
pub/doc/tools/clay.md
Normal file
@ -0,0 +1,207 @@
|
|||||||
|
`%clay`
|
||||||
|
========
|
||||||
|
|
||||||
|
## Paths
|
||||||
|
|
||||||
|
### Structure
|
||||||
|
|
||||||
|
Urbit paths have a very specific structure. First, since the clay
|
||||||
|
filesystem has a global namespace, the first element in any path
|
||||||
|
is the particular urbit whose filesystem you are trying to
|
||||||
|
access.
|
||||||
|
|
||||||
|
The second element specifies which desk you wish to access on
|
||||||
|
that urbit. Desks are independent branches (in the
|
||||||
|
revision-control sense) of their filesystem.
|
||||||
|
|
||||||
|
The third element specifies the revision number for that
|
||||||
|
desk. The remainder of the path is the path to the file.
|
||||||
|
|
||||||
|
Thus, a path in clay is:
|
||||||
|
|
||||||
|
`/urbit/desk/revision/path`.
|
||||||
|
|
||||||
|
For example, to get revision 5 of `/try/readme/md` off the `home`
|
||||||
|
desk on `~sampel-sipnym`, use:
|
||||||
|
|
||||||
|
`/~sampel-sipnym/home/5/try/readme/md`.
|
||||||
|
|
||||||
|
### Shortcuts
|
||||||
|
|
||||||
|
`%` refers to the current working
|
||||||
|
directory. `%%` refers to our parent, `%%%` refers to our
|
||||||
|
grandparent, and so forth.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
XX TBD
|
||||||
|
|
||||||
|
|
||||||
|
From the other direction, inserting a `=` into a path copies the
|
||||||
|
corresponding element from the current path into the path that
|
||||||
|
you're trying to access.
|
||||||
|
|
||||||
|
For example, if the current path is referencing our ship at the
|
||||||
|
current time, to reference `/try/readme`, use:
|
||||||
|
|
||||||
|
`/===try/readme`.
|
||||||
|
|
||||||
|
|
||||||
|
### Accessing commits
|
||||||
|
|
||||||
|
There are three ways to refer to particular commits in the
|
||||||
|
revision history. First, one can use the revision number.
|
||||||
|
Second, one can use any absolute time between the one numbered
|
||||||
|
commit and the next (inclusive of the first, exclusive of the
|
||||||
|
second). Thirdly, every desk has a map of labels to revision
|
||||||
|
numbers. These labels may be used to refer to specific commits.
|
||||||
|
|
||||||
|
|
||||||
|
## `ls`
|
||||||
|
|
||||||
|
`+ls /path` gives a directory listing at a path
|
||||||
|
|
||||||
|
## `cat`
|
||||||
|
|
||||||
|
`+cat /path`
|
||||||
|
prints out the file at the given path.
|
||||||
|
|
||||||
|
## `mount`
|
||||||
|
|
||||||
|
It's often useful to "mount" the clay filesystem to unix, so that
|
||||||
|
you can interact with it with the traditional unix tools. The
|
||||||
|
syntax to do this is as follows:
|
||||||
|
|
||||||
|
|mount /path [%mount-point]
|
||||||
|
|
||||||
|
This mirrors the desk out to unix in at the path
|
||||||
|
`<pier-directory> <mount-point>`. If you don't supply a
|
||||||
|
`%mount-point`, we use the last element in the path. Thus, if
|
||||||
|
you mount `%/pub/doc`, it'll by default put it in `doc`.
|
||||||
|
|
||||||
|
*The mount point is monitored Dropbox-style, so every change you
|
||||||
|
make to the file in unix is automatically commited to clay.*
|
||||||
|
|
||||||
|
You can unmount by specifying either the path or the mount point.
|
||||||
|
|
||||||
|
|unmount /path
|
||||||
|
|unmount %mount-point
|
||||||
|
|
||||||
|
## `merge`
|
||||||
|
|
||||||
|
Often, it's useful to be able to merge a desk into another desk.
|
||||||
|
The other desk does not, of course, need to be on the same urbit:
|
||||||
|
for example, the standard way to distribute an app is to put it
|
||||||
|
on a desk and let other people merge it into their own urbit.
|
||||||
|
|
||||||
|
The syntax is as follows:
|
||||||
|
|
||||||
|
|merge %to-desk ~from-urbit %from-desk [%strategy]
|
||||||
|
|
||||||
|
There are seven different merge strategies. Throughout our
|
||||||
|
discussion, we'll say that the merge is from Alice's desk to
|
||||||
|
Bob's.
|
||||||
|
|
||||||
|
### Native strategies
|
||||||
|
|
||||||
|
A `%init` merge should be used iff it's the first commit to a
|
||||||
|
desk. The head of Alice's desk is used as the number 1 commit to
|
||||||
|
Bob's desk. Obviously, the ancestry remains intact when
|
||||||
|
traversing the parentage of the commit, even though previous
|
||||||
|
commits are not numbered for Bob's desk.
|
||||||
|
|
||||||
|
A `%this` merge means to keep what's in Bob's desk, but join the
|
||||||
|
ancestry. Thus, the new commit has the head of each desk as
|
||||||
|
parents, but the data is exactly what's in Bob's desk. For those
|
||||||
|
following along in git, this is the 'ours' merge strategy, not
|
||||||
|
the '--ours' option to the 'recursive' merge strategy. In other
|
||||||
|
words, even if Alice makes a change that does not conflict with
|
||||||
|
Bob, we throw it away.
|
||||||
|
|
||||||
|
A `%that` merge means to take what's in Alice's desk, but join
|
||||||
|
the ancestry. This is the reverse of `%this`.
|
||||||
|
|
||||||
|
A `%fine` merge is a "fast-forward" merge. This succeeds iff one
|
||||||
|
head is in the ancestry of the other. In this case, we use the
|
||||||
|
descendant as our new head.
|
||||||
|
|
||||||
|
For `%meet`, `%mate`, and `%meld` merges, we first find the most
|
||||||
|
recent common ancestor to use as our merge base. If we have no
|
||||||
|
common ancestors, then we fail. If we have multiple most
|
||||||
|
recent common ancestors, then we have a criss-cross situation,
|
||||||
|
which should be handled delicately. At present, we don't handle
|
||||||
|
this kind of situation, but something akin to git's 'recursive'
|
||||||
|
strategy should be implemented in the future.
|
||||||
|
|
||||||
|
There's a functional inclusion ordering on `%fine`, `%meet`,
|
||||||
|
`%mate`, and `%meld` such that if an earlier strategy would have
|
||||||
|
succeeded, then every later strategy will produce the same
|
||||||
|
result. Put another way, every earlier strategy is the same as
|
||||||
|
every later strategy except with a restricted domain.
|
||||||
|
|
||||||
|
A `%meet` merge only succeeds if the changes from the merge base
|
||||||
|
to Alice's head (hereafter, "Alice's changes") are in different
|
||||||
|
files than Bob's changes. In this case, the parents are both
|
||||||
|
Alice's and Bob's heads, and the data is the merge base plus
|
||||||
|
Alice's changed files plus Bob's changed files.
|
||||||
|
|
||||||
|
A `%mate` merge attempts to merge changes to the same file when
|
||||||
|
both Alice and Bob change it. If the merge is clean, we use it;
|
||||||
|
otherwise, we fail. A merge between different types of changes --
|
||||||
|
for example, deleting a file vs changing it -- is always a
|
||||||
|
conflict. If we succeed, the parents are both Alice's and Bob's
|
||||||
|
heads, and the data is the merge base plus Alice's changed files
|
||||||
|
plus Bob's changed files plus the merged files.
|
||||||
|
|
||||||
|
A `%meld` merge will succeed even if there are conflicts. If
|
||||||
|
there are conflicts in a file, then we use the merge base's
|
||||||
|
version of that file, and we produce a set of files with
|
||||||
|
conflicts. The parents are both Alice's and Bob's heads, and the
|
||||||
|
data is the merge base plus Alice's changed files plus Bob's
|
||||||
|
changed files plus the successfully merged files plus the merge
|
||||||
|
base's version of the conflicting files.
|
||||||
|
|
||||||
|
### Metastrategies
|
||||||
|
|
||||||
|
There's also a meta-strategy `%auto`, which is the most common.
|
||||||
|
If no strategy is supplied, then `%auto` is assumed. `%auto`
|
||||||
|
checks to see if Bob's desk exists, and if it doesn't we use a
|
||||||
|
`%init` merge. Otherwise, we progressively try `%fine`,
|
||||||
|
`%meet`, and `%mate` until one succeeds.
|
||||||
|
|
||||||
|
If none succeed, we merge Bob's desk into a scratch desk. Then,
|
||||||
|
we merge Alice's desk into the scratch desk with the `%meld`
|
||||||
|
option to force the merge. For each file in the produced set of
|
||||||
|
conflicting files, we call the `++mash` function for the
|
||||||
|
appropriate mark, which annotates the conflicts if we know how.
|
||||||
|
|
||||||
|
Finally, we display a message to the user informing them of the
|
||||||
|
scratch desk's existence, which files have annotated conflicts,
|
||||||
|
and which files have unannotated conflicts. When the user has
|
||||||
|
resolved the conflicts, they can merge the scratch desk back into
|
||||||
|
Bob's desk. This will be a `%fine` merge since Bob's head is in
|
||||||
|
the ancestry of the scratch desk.
|
||||||
|
|
||||||
|
## Autosync
|
||||||
|
|
||||||
|
Since clay is reactive, it's possible for changes to the
|
||||||
|
filesystem to cause various actions. An important use of this is
|
||||||
|
in enabling "autosync". When a desk is synced to another, any
|
||||||
|
changes to the first desk are automatically applied to the
|
||||||
|
second.
|
||||||
|
|
||||||
|
This isn't simply mirroring, since the local desk might have
|
||||||
|
changes of its own. We use the full merge capabilities of clay
|
||||||
|
to try to make the merge clean. If there are conflicts, it'll
|
||||||
|
notify you and let you resolve them.
|
||||||
|
|
||||||
|
There can be complex sync flows, some of which are useful.
|
||||||
|
Often, many urbits will be synced to some upstream desk that is
|
||||||
|
trusted to provide updates. Sometimes, it's useful to sync two
|
||||||
|
desks to each other, so that changes to one or the other are
|
||||||
|
mirrored.
|
||||||
|
|
||||||
|
The syntax for syncing and unsyncing desks is as follows:
|
||||||
|
|
||||||
|
|sync %to-desk ~from-urbit %from-desk
|
||||||
|
|unsync %to-desk ~from-urbit %from-desk
|
548
pub/doc/tools/dojo.md
Normal file
548
pub/doc/tools/dojo.md
Normal file
@ -0,0 +1,548 @@
|
|||||||
|
|
||||||
|
# `:dojo`
|
||||||
|
|
||||||
|
The dojo is a typed functional shell. Its prompt is:
|
||||||
|
|
||||||
|
~urbit-name:dojo>
|
||||||
|
|
||||||
|
|
||||||
|
### Quickstart
|
||||||
|
|
||||||
|
To print a Hoon expression or other recipe:
|
||||||
|
|
||||||
|
~urbit-name:dojo> (add 2 2)
|
||||||
|
|
||||||
|
To save a recipe as a variable `foo`:
|
||||||
|
|
||||||
|
~urbit-name:dojo> =foo (add 2 2)
|
||||||
|
|
||||||
|
To save as a unix file (`$pier/.urb/put/foo/bar.baz`):
|
||||||
|
|
||||||
|
~urbit-name:dojo> .foo/bar/baz (add 2 2)
|
||||||
|
|
||||||
|
To save as an urbit file (`/===/foo/bar/baz`):
|
||||||
|
|
||||||
|
~urbit-name:dojo> *foo/bar/baz (add 2 2)
|
||||||
|
|
||||||
|
A noun generator with ordered and named arguments:
|
||||||
|
|
||||||
|
~urbit-name:dojo> +make one two three, =foo (add 2 2), =bar 42
|
||||||
|
|
||||||
|
A poke message to an urbit daemon:
|
||||||
|
|
||||||
|
~urbit-name:dojo> :~urbit-name/talk (add 2 2)
|
||||||
|
|
||||||
|
A system command to `:hood`:
|
||||||
|
|
||||||
|
~urbit-name:dojo> |reload %vane
|
||||||
|
|
||||||
|
### Manual
|
||||||
|
|
||||||
|
An Urbit value is called a "noun." A noun is either an unsigned
|
||||||
|
integer ("atom") or an ordered pair of nouns ("cell"). Nouns
|
||||||
|
are just values, with no cyclic structure or pointer identity.
|
||||||
|
|
||||||
|
The dojo is your safe space for hand-to-hand combat with nouns.
|
||||||
|
Every dojo command builds a "product" noun functionally, then
|
||||||
|
applies this product in a side effect -- show, save, or send.
|
||||||
|
|
||||||
|
#### Theory
|
||||||
|
|
||||||
|
The dojo is not just a Hoon interpreter. Hoon is a purely
|
||||||
|
functional language; dojo recipes are *conceptually* functional,
|
||||||
|
but they often use concrete actions or interactions. A simple
|
||||||
|
Hoon expression is only one kind of recipe.
|
||||||
|
|
||||||
|
A recipe can get data from an HTTP GET request or an interactive
|
||||||
|
input dialog. It can also query, even block on, the Urbit
|
||||||
|
namespace. These operations are *conceptually* functional, but
|
||||||
|
not *actually* functional. They don't belong in a pure Hoon
|
||||||
|
expression, but they do belong in a dojo recipe. A recipe is "FP
|
||||||
|
in the large," more like Unix pipes than Haskell monads.
|
||||||
|
|
||||||
|
The dojo is "single-threaded" in each session. One session can
|
||||||
|
work on one command at a time. The session does not accept user
|
||||||
|
input while processing a command, even when it blocks over the
|
||||||
|
network. And each session's state is independent. (If you want
|
||||||
|
to work on two things at a time, connect two console sessions to
|
||||||
|
your dojo.)
|
||||||
|
|
||||||
|
Once you've built your product noun, you show, save, or send it.
|
||||||
|
|
||||||
|
You can pretty-print the product to the console. You can save it
|
||||||
|
-- as a dojo variable, as a revision to the Urbit filesystem, or
|
||||||
|
as an export to a file in the Unix filesystem. Or you can
|
||||||
|
send it -- staying native with an Urbit poke, or going retro
|
||||||
|
with an HTTP PUT/POST.
|
||||||
|
|
||||||
|
All these operations are typed. Hoon is a statically typed
|
||||||
|
language, but the dojo is a dynamic interpreter. The nouns you
|
||||||
|
build in the dojo are dynamically typed nouns, or "cages".
|
||||||
|
|
||||||
|
A cage actually has two layers of type: "mark," a network label
|
||||||
|
(like a MIME type), and "range," a Hoon language type. When a
|
||||||
|
cage is sent across the Urbit network, the receiving daemon
|
||||||
|
validates the noun against its own version of the mark, and
|
||||||
|
regenerates the range.
|
||||||
|
|
||||||
|
Of course, sometimes a recipe produces a noun with mark `%noun`,
|
||||||
|
meaning "any noun," and range `*`, the set of all nouns. We have
|
||||||
|
no choice but to do the best we can with mystery nouns, but we
|
||||||
|
prefer a formal description.
|
||||||
|
|
||||||
|
Marks let us perform a variety of formal typed operations on
|
||||||
|
nouns: validation of untrusted data, format conversion, even
|
||||||
|
patch and diff for revision control.
|
||||||
|
|
||||||
|
#### Other resources
|
||||||
|
|
||||||
|
An excellent way to understand `:dojo` is to read the source,
|
||||||
|
which is in `/===/ape/dojo/hoon`.
|
||||||
|
|
||||||
|
Unfortunately, you may or may not know Hoon. We'll use some Hoon
|
||||||
|
snippets here for defining structures and grammars. Just think
|
||||||
|
of it as pseudocode -- the meaning should be clear from context.
|
||||||
|
|
||||||
|
#### Syntax and semantics
|
||||||
|
|
||||||
|
To use the dojo, type a complete command at the dojo prompt.
|
||||||
|
The simplest command just prints a Hoon expression:
|
||||||
|
|
||||||
|
~urbit-name:dojo> (add 2 2)
|
||||||
|
|
||||||
|
Hit return. You'll see:
|
||||||
|
|
||||||
|
> (add 2 2)
|
||||||
|
4
|
||||||
|
~urbit-name:dojo>
|
||||||
|
|
||||||
|
Similarly in tall form,
|
||||||
|
|
||||||
|
~urbit-name:dojo> %+ add 2 2
|
||||||
|
> %+ add 2 2
|
||||||
|
4
|
||||||
|
~urbit-name:dojo>
|
||||||
|
|
||||||
|
An incomplete command goes into a multiline input buffer. Use
|
||||||
|
the up-arrow (see the console history section) to get the last
|
||||||
|
command back, edit it so it's just `%+ add 2`, and press return.
|
||||||
|
You'll see:
|
||||||
|
|
||||||
|
> %+ add 2
|
||||||
|
~urbit-name/dojo<
|
||||||
|
|
||||||
|
Enter `2`. You'll see:
|
||||||
|
|
||||||
|
> %+ add 2
|
||||||
|
2
|
||||||
|
4
|
||||||
|
~urbit-name/dojo>
|
||||||
|
|
||||||
|
The full command that parses and runs is the concatenation of all
|
||||||
|
the partial lines, with a space inserted between them. To clear
|
||||||
|
all multiline input, just hit return on an empty prompt.
|
||||||
|
|
||||||
|
##### Command structure
|
||||||
|
|
||||||
|
Every finished line is parsed into one `++dojo-command`:
|
||||||
|
|
||||||
|
++ dojo-command ::
|
||||||
|
$% [%edit p=path q=dojo-recipe] :: modify clay file
|
||||||
|
[%http p=? q=purl r=dojo-recipe] :: http post or put
|
||||||
|
[%poke p=goal q=dojo-recipe] :: send request
|
||||||
|
[%save p=path q=dojo-recipe] :: replace clay file
|
||||||
|
[%show p=dojo-recipe] :: print to console
|
||||||
|
[%unix p=path q=dojo-recipe] :: export to unix
|
||||||
|
[%verb p=term q=dojo-recipe] :: store variable
|
||||||
|
== ::
|
||||||
|
|
||||||
|
Each kind of `++dojo-command` is an action that depends on one
|
||||||
|
noun thproduction, a `++dojo-recipe`. We describe first the
|
||||||
|
commands, then the recipes.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
###### `[%show p=dojo-recipe]`
|
||||||
|
|
||||||
|
To print the product, the command is just the recipe:
|
||||||
|
|
||||||
|
~urbit-name:dojo> (add 2 2)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
###### `[%verb p=term q=dojo-recipe]`
|
||||||
|
|
||||||
|
To save the product to a variable `foo`:
|
||||||
|
|
||||||
|
~urbit-name:dojo> =foo (add 2 2)
|
||||||
|
|
||||||
|
`foo` goes into your Hoon subject (scope) and is available to all
|
||||||
|
expressions.
|
||||||
|
|
||||||
|
To unbind `foo`:
|
||||||
|
|
||||||
|
~urbit-name:dojo> =foo
|
||||||
|
|
||||||
|
The dojo has a set of special variables, some read-write and some
|
||||||
|
read-only: `dir`, `lib`, `arc`, `now`, `our`.
|
||||||
|
|
||||||
|
The read-write specials are `dir`, `lib` and `arc`. `dir` is the beak
|
||||||
|
(revision-control branch) and directory this session is operating in,
|
||||||
|
and normally accessed/set with `%`. `lib` is a set of libraries, and
|
||||||
|
`arc` a set of structures, to put in the Hoon subject.
|
||||||
|
|
||||||
|
Read-only specials are `now`, the current (128-bit `@da`) time,
|
||||||
|
and `our`, the current urbit.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
###### `[%edit p=path q=dojo-recipe]`
|
||||||
|
###### `[%save p=path q=dojo-recipe]`
|
||||||
|
|
||||||
|
The product is either a new version of, or a modification to,
|
||||||
|
the Urbit file at the given path. (See the discussion of Urbit
|
||||||
|
filesystem paths.)
|
||||||
|
|
||||||
|
To save:
|
||||||
|
|
||||||
|
~urbit-name:dojo> *%/numbers/four (add 2 2)
|
||||||
|
|
||||||
|
To edit:
|
||||||
|
|
||||||
|
~urbit-name:dojo> -%/numbers/four (add 2 2)
|
||||||
|
|
||||||
|
A save (`*`) overwrites the current (if any) version of the file
|
||||||
|
with a new version of any mark. The save command above will work
|
||||||
|
(if you want `/numbers/four` at your current path).
|
||||||
|
|
||||||
|
An edit (`-`) produces a diff whose mark has to match the diff
|
||||||
|
mark for the current version of the file. The edit command above
|
||||||
|
will not work, because evaluating a Hoon expression like `(add 2
|
||||||
|
2)` just produces a `%noun` mark, ie, an arbitrary noun.
|
||||||
|
|
||||||
|
For either saves or edits, the current version of the file must
|
||||||
|
be the same version specified in the write -- in other words,
|
||||||
|
we can only write to HEAD. If someone else has sneaked in a
|
||||||
|
change since the version specified, the command will fail.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
###### `[%unix p=path q=dojo-recipe]`
|
||||||
|
|
||||||
|
~urbit-name:dojo> ./numbers/four (add 2 2)
|
||||||
|
|
||||||
|
The product is saved as a Unix file (its mark is translated
|
||||||
|
to MIME, and the MIME type is mapped as the extension).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
###### `[%poke p=goal q=dojo-recipe]`
|
||||||
|
|
||||||
|
A poke is a one-way transactional request. It either succeeds
|
||||||
|
and returns no information, or fails and produces an error dump.
|
||||||
|
|
||||||
|
Every poke is sent to one daemon on one urbit. The default urbit
|
||||||
|
is your urbit. The default daemon is the system daemon, `:hood`.
|
||||||
|
The following syntactic forms are equivalent:
|
||||||
|
|
||||||
|
~urbit-name:dojo> :~urbit-name/hood (add 2 2)
|
||||||
|
~urbit-name:dojo> :hood (add 2 2)
|
||||||
|
~urbit-name:dojo> :~urbit-name (add 2 2)
|
||||||
|
~urbit-name:dojo> : (add 2 2)
|
||||||
|
|
||||||
|
Urbit pokes do not have a separate verb. The mark of the message
|
||||||
|
defines the semantics of the operation. You don't call a method
|
||||||
|
`foo` whose argument is a noun in mark `bar` -- you poke a noun
|
||||||
|
in mark `bar`. The mark is the protocol is the method.
|
||||||
|
|
||||||
|
If the poke succeeds, you'll see an `>=` line. If not, you'll
|
||||||
|
see an error report, typically with a stack trace.
|
||||||
|
|
||||||
|
It's common (but not necessary) to use a custom generator for the
|
||||||
|
daemon you're talking to. (For generators, see below.) Hence
|
||||||
|
|
||||||
|
~urbit-name:dojo> :~urbit-name/fish +fish/catch (add 2 2)
|
||||||
|
|
||||||
|
It's irritating to type "fish" twice, just because we're using a
|
||||||
|
fish generator to talk to a fish daemon. Hence a shortcut:
|
||||||
|
|
||||||
|
~urbit-name:dojo> :~urbit-name/fish|catch (add 2 2)
|
||||||
|
|
||||||
|
If we combine all these defaults, we get the "system command"
|
||||||
|
shortcut:
|
||||||
|
|
||||||
|
~urbit-name:dojo> :~urbit-name/hood +hood/reload %ames
|
||||||
|
~urbit-name:dojo> |reload %ames
|
||||||
|
|
||||||
|
This is the most common poke, a generated message to your own
|
||||||
|
hood.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
##### `[%http p=? q=purl r=dojo-recipe]`
|
||||||
|
|
||||||
|
The Web has its own poke, unfortunately in two flavors. To POST,
|
||||||
|
|
||||||
|
~urbit-name:dojo> +http://website.com (add 2 2)
|
||||||
|
|
||||||
|
To PUT:
|
||||||
|
|
||||||
|
~urbit-name:dojo> -http://website.com (add 2 2)
|
||||||
|
|
||||||
|
As with a poke, you'll get a >= for success, or an error report.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
##### Recipes, models and filters
|
||||||
|
|
||||||
|
But wait, what's a recipe? Simplifying the actual code slightly:
|
||||||
|
|
||||||
|
++ dojo-recipe :: functional build
|
||||||
|
$% [%ex p=twig] :: hoon expression
|
||||||
|
[%as p=mark q=dojo-recipe] :: conversion
|
||||||
|
[%do p=twig q=dojo-recipe] :: apply gate
|
||||||
|
[%ge p=dojo-script] :: generator
|
||||||
|
[%ur p=purl] :: get url
|
||||||
|
[%tu p=(list dojo-recipe)] :: tuple
|
||||||
|
== ::
|
||||||
|
++ dojo-script :: script
|
||||||
|
$: p=path :: core recipe
|
||||||
|
q=dojo-config :: configuration
|
||||||
|
== ::
|
||||||
|
++ dojo-config :: configuration
|
||||||
|
$: p=(list dojo-recipe) :: by order
|
||||||
|
q=(map term (unit dojo-recipe)) :: by keyword
|
||||||
|
== ::
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
###### `[%ex p=twig]`
|
||||||
|
|
||||||
|
The twig in an `%ex` recipe is a Hoon expression. The recipe
|
||||||
|
syntax is just the Hoon syntax.
|
||||||
|
|
||||||
|
The subject of the twig is a core stack: first the Hoon kernel,
|
||||||
|
then the Arvo standard library, then the structures and libraries
|
||||||
|
in `lib` and `arc`. On the very top are the dojo variables.
|
||||||
|
|
||||||
|
A twig produces the trivial mark `%noun`, except in two cases
|
||||||
|
where the dojo can do better. The dojo analyzes the twig to
|
||||||
|
detect two trivial cases where direct evaluation gives us a mark:
|
||||||
|
a variable reference like `foo` that matches a dojo variable, or
|
||||||
|
an urbitspace dereference like `.^(/cx/~urbit-name/main/1/foo)`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
###### `[%tu p=(list dojo-recipe)]`
|
||||||
|
|
||||||
|
A is just a tuple of recipes, using the normal Hoon syntax for
|
||||||
|
a tuple. `[a]` is `a`, `[a b]` the cell `[a b]`, `[a b c]` the
|
||||||
|
cell `[a [b c]]`.
|
||||||
|
|
||||||
|
A tuple, unless it's a trivial 1-tuple, is always marked `%noun`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
###### `[%ge p=dojo-script]`
|
||||||
|
|
||||||
|
A `%ge` is a generator, a configurable script loaded from the
|
||||||
|
filesystem.
|
||||||
|
|
||||||
|
The script recipe `++dojo-script` specifies a script path, a list
|
||||||
|
of ordered arguments, and a list of keyword arguments. All the
|
||||||
|
arguments are recipes. The path specifies a Hoon source file in
|
||||||
|
`/===/gen/[path]`.
|
||||||
|
|
||||||
|
For the path `/fun/make`, the ordered arguments `1`, `2` and `3`,
|
||||||
|
and the named arguments `foo` and `bar`, the syntax is:
|
||||||
|
|
||||||
|
~urbit-name:dojo> +fun/make 1 2 3, =foo (add 2 2), =bar 42
|
||||||
|
|
||||||
|
Unless this non-closed form is the end of a command, it needs to
|
||||||
|
be surrounded by `[]` to make it play well with others.
|
||||||
|
|
||||||
|
Generator programming is covered in the dojo developer's guide.
|
||||||
|
The user doesn't need to know or notice how the generator gets
|
||||||
|
its input (if any), except in one case: a dialog.
|
||||||
|
|
||||||
|
A dialog generator will take over the prompt and ask you
|
||||||
|
questions. If this seems terrifying, ^D will abort the dialog,
|
||||||
|
the recipe, and the command, and take you back to the dojo.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
###### `[%as p=mark q=dojo-recipe]`
|
||||||
|
|
||||||
|
`%as` is a mark conversion. Since the input to it is another
|
||||||
|
recipe, we can chain them to make a conversion pipeline.
|
||||||
|
|
||||||
|
To convert a recipe, just precede it with the converison form, `&mark`:
|
||||||
|
|
||||||
|
~urbit-name:dojo> &noun (add 2 2)
|
||||||
|
~urbit-name:dojo> &md (add 50 7)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
###### `[%do p=twig q=dojo-recipe]`
|
||||||
|
|
||||||
|
`%do` is a Hoon functino (gate) application. It can also be in a pipeline.
|
||||||
|
|
||||||
|
Its syntax is a hoon expression preceeded by `_`:
|
||||||
|
|
||||||
|
~urbit-name:dojo> _lore 'hello\0aworld'
|
||||||
|
~urbit-name:dojo> _|=(a=@ (mul 3 a))} (add 2 2)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
###### `[%ur p=purl]`
|
||||||
|
|
||||||
|
A simple HTTP get produces the result as a `%httr` noun.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Development
|
||||||
|
|
||||||
|
Developing dojo generators is the easiest form of Hoon programming.
|
||||||
|
Generator scripts are found in the `gen` folder.
|
||||||
|
|
||||||
|
#### Configuration
|
||||||
|
|
||||||
|
All generator scripts are configured with the same configuration gate:
|
||||||
|
|
||||||
|
|= $: [now=@da eny=@ bec=beak]
|
||||||
|
[arg=my-arguments opt=my-options]
|
||||||
|
==
|
||||||
|
|
||||||
|
We try to minimize boilerplate, but you can't get out of this
|
||||||
|
one. The dojo will slam this configuration gate to create your
|
||||||
|
generator.
|
||||||
|
|
||||||
|
The head of the sample is a system context. `now` is the date of
|
||||||
|
the call; `eny` is 256 bits of entropy; `bec` is a triple
|
||||||
|
`[p=ship q=desk r=case]` (ie, the root of a filesystem path).
|
||||||
|
This beak is the path to the script, not the current path within
|
||||||
|
the dojo (dojo variables are not, unlike in Unix, visible to
|
||||||
|
generator scripts).
|
||||||
|
|
||||||
|
`arg` and `opt` are whatever you want them to be. (Use `~` if
|
||||||
|
you have no arguments or options.) The dojo will replace `arg`
|
||||||
|
with the user's ordered arguments, and replace any options in
|
||||||
|
`opt` specified by the user's named arguments. (More exactly,
|
||||||
|
if the user specifies `=foo 42`, your `opt` is replaced with
|
||||||
|
`opt(foo 42)`.)
|
||||||
|
|
||||||
|
Bear in mind that dojo syntax is list-centric, so your `arg` will
|
||||||
|
always end with a `~`. For instance,
|
||||||
|
|
||||||
|
~urbit-name/dojo> +fun/make 1 2 3
|
||||||
|
|
||||||
|
will generate an `arg` of `[1 2 3 ~]`. Yes, this is the only
|
||||||
|
place in Urbit where we do list-centric arguments.
|
||||||
|
|
||||||
|
Note also that script configuration is typed. The user's command
|
||||||
|
will fail if there's a type mismatch. But `arg` does not have to
|
||||||
|
be a homogeneous list -- just a tuple with `~` on the end. Also,
|
||||||
|
you can use `arg=*` and sort out the nouns by hand.
|
||||||
|
|
||||||
|
You can also use `*` anywhere if you're not interested in the
|
||||||
|
system context, or in
|
||||||
|
|
||||||
|
#### Generators
|
||||||
|
|
||||||
|
There are three kinds of generators: builders (with no special
|
||||||
|
I/O), dialogs (which read console input), and scrapers (which
|
||||||
|
pull data from the webs). Any generator can use `.^` to both
|
||||||
|
read from and block (wait for remote or delayed results) on
|
||||||
|
the Urbit global namespace.
|
||||||
|
|
||||||
|
A generator produces a cell whose tail is the configuration gate,
|
||||||
|
and whose head is either `%say` for a builder, `%ask` for a
|
||||||
|
dialog, or `%get` for a scraper.
|
||||||
|
|
||||||
|
(If you want to write one generator which both prompts the user
|
||||||
|
and scrapes the web, don't. Write two, and configure the second
|
||||||
|
with the result of the first. We pay a price for keeping things
|
||||||
|
stupid.)
|
||||||
|
|
||||||
|
##### Builders
|
||||||
|
|
||||||
|
A builder just produces a cask (mark-value cell) directly from
|
||||||
|
the configuration gate. Here's the simplest builder, with a
|
||||||
|
blank configuration:
|
||||||
|
|
||||||
|
:- %say |= *
|
||||||
|
:- %noun
|
||||||
|
"hello, world."
|
||||||
|
|
||||||
|
##### Dialogs
|
||||||
|
|
||||||
|
A dialog is a console input form. We recommend using the helpful
|
||||||
|
`sole` structures, with
|
||||||
|
|
||||||
|
/- *sole
|
||||||
|
|
||||||
|
(If you're interested in building your own dialogs without `sole`
|
||||||
|
(not that complicated at all), it's easiest to start by
|
||||||
|
reverse-engineering `sole`.)
|
||||||
|
|
||||||
|
Otherwise, a dialog boilerplate (with blank configuration), which
|
||||||
|
generates a `my-result` result with mark `%my-result-mark`:
|
||||||
|
|
||||||
|
:- %ask |= *
|
||||||
|
^- (sole-result (cask my-result))
|
||||||
|
%+ sole-so %my-result-mark
|
||||||
|
*my-result
|
||||||
|
|
||||||
|
Internally, a `++sole-result` is either a final result or a new
|
||||||
|
dialog core with a new prompt. The dojo keeps running the dialog
|
||||||
|
until it produces a final result.
|
||||||
|
|
||||||
|
A dialog step can do one of three things: print a message, prompt
|
||||||
|
for a value, or finish with a result. These are done with
|
||||||
|
`sole-yo`, `sole-lo`, and `sole-so` respectively. Here's a
|
||||||
|
simple dialog which uses all of them:
|
||||||
|
|
||||||
|
:- %ask |= *
|
||||||
|
^- (sole-result (cask ,@ud))
|
||||||
|
%+ sole-yo leaf/"let's multiply two numbers..."
|
||||||
|
%+ sole-lo [%& %number "number one: "]
|
||||||
|
%+ sole-go dim:ag
|
||||||
|
|= one=@ud
|
||||||
|
%+ sole-lo [%& %number "number two: "]
|
||||||
|
%+ sole-go dim:ag
|
||||||
|
|= two=@ud
|
||||||
|
%+ sole-so %noun
|
||||||
|
(mul one two)
|
||||||
|
|
||||||
|
`++sole-yo` prints a tank (prettyprint structure). See `++tank`
|
||||||
|
in hoon.hoon.
|
||||||
|
|
||||||
|
`++sole-lo` takes a prompt and a new dialog. In the example,
|
||||||
|
`[%& %number "your number: "]` is a `++sole-prompt`. `&` as
|
||||||
|
opposed to `|` means input is echoed (not a password).
|
||||||
|
`%number` is a history label; all inputs with the same label
|
||||||
|
share the same history buffer.
|
||||||
|
|
||||||
|
The `++sole-dialog` is generally built with `++sole-go`, as used
|
||||||
|
above. This takes a parsing `++rule` (here `dim:ag`, which
|
||||||
|
parses a decimal), and a gate whose sample is the parsed value,
|
||||||
|
producing a new dialog.
|
||||||
|
|
||||||
|
##### Scrapers
|
||||||
|
|
||||||
|
Most stuff on the internets is crap, but there's exceptions.
|
||||||
|
Sometimes it's nice to get it and compute functions on it.
|
||||||
|
|
||||||
|
A scraper is much like a dialog, except instead of `sole-lo` and
|
||||||
|
`sole-go` it uses `sole-at`.
|
||||||
|
|
||||||
|
:- %get |= *
|
||||||
|
%+ sole-yo leaf/"Fetching example.com"
|
||||||
|
%+ sole-at [[& ~ `/com/example] `/ ~]
|
||||||
|
|= hit=httr
|
||||||
|
%+ sole-yo leaf/"Fetched."
|
||||||
|
%+ sole-so %httr
|
||||||
|
hit
|
||||||
|
|
||||||
|
`++sole-at` takes a `purl` request url, and a gate through
|
||||||
|
which to slam the result `httr`.
|
287
pub/doc/tools/talk.md
Normal file
287
pub/doc/tools/talk.md
Normal file
@ -0,0 +1,287 @@
|
|||||||
|
# `:talk`
|
||||||
|
|
||||||
|
`:talk` is the Urbit appliance for chatter and notifications.
|
||||||
|
For less sophisticated users, Urbit *is* just `:talk`. If you
|
||||||
|
see `:talk` as "like Slack, but distributed," or "like IRC, but
|
||||||
|
persistent and encrypted," you're not completely wrong.
|
||||||
|
|
||||||
|
`:talk` is an unusual messenger in two ways. One: by default, it
|
||||||
|
multiplexes all content streams into a single flow. Most UI
|
||||||
|
researchers agree that context-switching is cognitively expensive
|
||||||
|
and leads to surfing the Internet. (`:talk` is also used for
|
||||||
|
your system notifications.)
|
||||||
|
|
||||||
|
Two: text lines are limited to 64 ASCII bytes, no uppercase.
|
||||||
|
This restriction is mobile-friendly and reduces the aesthetic
|
||||||
|
impact of low-quality content.
|
||||||
|
|
||||||
|
Messages in `:talk` are called "posts". Posts go to "stations,"
|
||||||
|
which are just like IRC or Slack channels. Any urbit can host or
|
||||||
|
subscribe to any number of stations.
|
||||||
|
|
||||||
|
`:talk` is not a text-only messenger; it's designed to support
|
||||||
|
arbitrary content in posts, from URLs to images to long-form
|
||||||
|
text. (Only URLs right now.) However, any message on `:talk`
|
||||||
|
has to be able to summarize itself in a 64-byte text line.
|
||||||
|
|
||||||
|
There are four kinds of station: a write-only "mailbox" for
|
||||||
|
direct messages, an invite-only "party" for private conversation,
|
||||||
|
a read-only "journal" for curated content, and a public-access
|
||||||
|
"board" for general use or abuse.
|
||||||
|
|
||||||
|
While there's obviously no central `:talk` server for all of
|
||||||
|
Urbit, and thus no such thing as a truly global station space,
|
||||||
|
active Urbit stars cooperate to federate, manage and mirror a
|
||||||
|
collectively-managed namespace, very like Usenet. These
|
||||||
|
"federal" stations are generally public-access boards.
|
||||||
|
|
||||||
|
### Quickstart
|
||||||
|
|
||||||
|
Let's post something! At the default `:talk` prompt
|
||||||
|
```
|
||||||
|
~tasfyn-partyv:talk:
|
||||||
|
```
|
||||||
|
type the message:
|
||||||
|
```
|
||||||
|
~tasfyn-partyv:talk: hello, world.
|
||||||
|
```
|
||||||
|
And hit return. Don't worry, no one but you will see this. The
|
||||||
|
`:` means you're posting to yourself. You'll get the post:
|
||||||
|
```
|
||||||
|
~tasfyn-partyv: hello, world.
|
||||||
|
~tasfyn-partyv:talk:
|
||||||
|
```
|
||||||
|
It's boring to post to yourself. Let's join a station:
|
||||||
|
```
|
||||||
|
~tasfyn-partyv: ;join /urbit-test
|
||||||
|
```
|
||||||
|
(`/urbit-test` is a federal station, meaning it's hosted by your
|
||||||
|
star (for `~tasfyn-partyv`, `~doznec`). The `/` notation is just
|
||||||
|
an abbreviation for `~doznec/urbit-test`.)
|
||||||
|
|
||||||
|
You'll see:
|
||||||
|
```
|
||||||
|
---------:talk| %porch subscribed to /urbit-test, called `>`
|
||||||
|
---------:talk| rules of /urbit-test:
|
||||||
|
---------:talk| test posts only. no shitposting. no pedos/nazis.
|
||||||
|
~doznec> ~tasfyn-partyv admitted to %urbit-test
|
||||||
|
~tasfyn-partyv:talk>
|
||||||
|
```
|
||||||
|
Notice the character assignment - stations you're subscribed to are
|
||||||
|
assigned [consistent ASCII glyphs](#-station-glyphs), which you'll
|
||||||
|
see in the log when you hear from these stations, and on the prompt
|
||||||
|
when you're talking to them.
|
||||||
|
|
||||||
|
Post a line to `/urbit-test`:
|
||||||
|
```
|
||||||
|
~tasfyn-partyv:talk> hello, world
|
||||||
|
```
|
||||||
|
You'll see, echoed back at you through `~doznec`:
|
||||||
|
```
|
||||||
|
~tasfyn-partyv:talk> hello, world
|
||||||
|
```
|
||||||
|
And of course, anyone else in `/urbit-test` will see it as well.
|
||||||
|
But you don't care about `/urbit-test`, so leave it:
|
||||||
|
```
|
||||||
|
~tasfyn-partyv:talk> ;leave
|
||||||
|
```
|
||||||
|
You'll see:
|
||||||
|
```
|
||||||
|
---------:talk| %porch has left /urbit-test, called `>`
|
||||||
|
```
|
||||||
|
Everyone else will see:
|
||||||
|
```
|
||||||
|
~doznec> ~tasfyn-partyv has left %urbit-test
|
||||||
|
```
|
||||||
|
|
||||||
|
Now you're ready to use `:talk` for real! List the federal
|
||||||
|
groups currently available with
|
||||||
|
```
|
||||||
|
~tasfyn-partyv:talk> ;list
|
||||||
|
```
|
||||||
|
For general discussion about Urbit, we recommend `/urbit-meta`.
|
||||||
|
|
||||||
|
### Basic usage
|
||||||
|
|
||||||
|
#### Input conventions
|
||||||
|
|
||||||
|
There are three kinds of inputs you can type at the `:talk`
|
||||||
|
prompt: lines, URLs, and commands.
|
||||||
|
|
||||||
|
A line is 64 bytes of ASCII lowercase and spaces. If the line
|
||||||
|
starts with '@', it's an action (IRC `/me`).
|
||||||
|
|
||||||
|
The `:talk` interface will let you keep typing past 64 bytes, but
|
||||||
|
insert a Unicode bullet-point character in an appropriate space
|
||||||
|
in your post, to show you the prospective linebreak. Your essay
|
||||||
|
will be posted in multiple lines.
|
||||||
|
|
||||||
|
A URL is any valid URL. A command is any line starting with `;`.
|
||||||
|
|
||||||
|
#### Source annotation
|
||||||
|
|
||||||
|
Any post in your flow is shown with its author, together with a
|
||||||
|
glyph that shows how the post reached you. A post can reach you
|
||||||
|
in one of three ways:
|
||||||
|
|
||||||
|
Any post you see reached you in one of three ways. Either it was
|
||||||
|
sent directly to just you; to you and others; or to a station you
|
||||||
|
subscribe to.
|
||||||
|
|
||||||
|
Posts to just you are `:`. Posts to you and others (a multiparty
|
||||||
|
conversation) are `*`, unless you've bound this conversation to a
|
||||||
|
glyph. Posts to a station use that station's glyph.
|
||||||
|
|
||||||
|
You can see a list of glyph bindings with `;what`. Write
|
||||||
|
`;what >` to see what station `>` is bound to, or
|
||||||
|
`;what /urbit-test` to see if `/urbit-test` has a binding.
|
||||||
|
|
||||||
|
#### Audience selection
|
||||||
|
|
||||||
|
Audience selection is important in a multiplexed communicator!
|
||||||
|
The audience is always shown in your prompt. If there's a glyph
|
||||||
|
for it, it's shown as the glyph:
|
||||||
|
```
|
||||||
|
~tasfyn-partyv:talk>
|
||||||
|
```
|
||||||
|
Otherwise, the audience is shown in parens:
|
||||||
|
```
|
||||||
|
~tasfyn-partyv:talk(~wictuc-folrex)
|
||||||
|
```
|
||||||
|
|
||||||
|
`:talk` works fairly hard to get the audience right and minimize
|
||||||
|
manual switching. But to manually set the audience, the command
|
||||||
|
is simply `;station` - eg, `;~wictuc-folrex` for a direct post;
|
||||||
|
`/urbit-test` or `~doznec/urbit-test` to post to a federal
|
||||||
|
station, `%mystation` to post to a station on your own ship.
|
||||||
|
For a station bound to a glyph, `;` then the glyph; eg, `;>`.
|
||||||
|
|
||||||
|
You can post a line and set the audience in one command, eg:
|
||||||
|
```
|
||||||
|
;~wictuc-folrex this is a private message
|
||||||
|
```
|
||||||
|
|
||||||
|
You can configure your audience in a number of ways, which are
|
||||||
|
applied in priority order. From strongest to weakest:
|
||||||
|
|
||||||
|
- if typing a post, the audience when you started typing.
|
||||||
|
- if you activated a post (see below), the post you activated.
|
||||||
|
- if you manually locked the audience (see above), that audience.
|
||||||
|
- audience of the last post received.
|
||||||
|
- audience of the last post sent.
|
||||||
|
|
||||||
|
You can clear any audience setting layer by moving your cursor to
|
||||||
|
the start of the line and pressing backspace (whether the line is
|
||||||
|
empty or not). Posting a line clears the typing and activation
|
||||||
|
configurations.
|
||||||
|
|
||||||
|
#### Post activation and numbering
|
||||||
|
|
||||||
|
Every post can summarize itself in 64 bytes. But some posts
|
||||||
|
contain more information, which is not displayed by default.
|
||||||
|
Displaying this "attachment" is an opt-in operation. In the
|
||||||
|
post, it's marked by an underscore `_`, instead of a space,
|
||||||
|
between source and content.
|
||||||
|
|
||||||
|
The conventional example is a URL. When you post a URL:
|
||||||
|
```
|
||||||
|
~tasfyn-partyv:talk> http://foobar.com/moo/baz
|
||||||
|
```
|
||||||
|
This will appear in the flow as:
|
||||||
|
```
|
||||||
|
~tasfyn-partyv>_foobar.com
|
||||||
|
```
|
||||||
|
meaning that `~tasfyn-partyv` posted a link to `foobar.com`,
|
||||||
|
on the station or conversation whose glyph is `>`.
|
||||||
|
|
||||||
|
The effect of activating a post depends on the post. For a link,
|
||||||
|
the full URL is shown and (system permitting) put into the OS's
|
||||||
|
clipboard, or even automatically navigated to. Even for a text
|
||||||
|
post, activating shows the full audience, for complex audiences.
|
||||||
|
|
||||||
|
Posts in your `:talk` flow are numbered; the numbers are printed
|
||||||
|
every five posts, as
|
||||||
|
```
|
||||||
|
----------[5955]
|
||||||
|
```
|
||||||
|
You can specify a post to activate in two ways: by absolute or
|
||||||
|
relative position. Absolute position is a direct history number:
|
||||||
|
```
|
||||||
|
;5955
|
||||||
|
```
|
||||||
|
If you use fewer digits than are in the current flow number, the
|
||||||
|
high digits are defaulted "deli style" - if the current number is
|
||||||
|
5955, typing `;3` means `;5953`, and `;140` means `;5140`. To
|
||||||
|
actually activate post `3`, write `;0003`.
|
||||||
|
|
||||||
|
A unary sequence of `;` characters looks backward from the
|
||||||
|
present. `;` activates the most recent post; `;;` the second
|
||||||
|
most recent; etc.
|
||||||
|
|
||||||
|
#### Nicknames
|
||||||
|
|
||||||
|
Sometimes you know your Urbit friends by other names, on or
|
||||||
|
offline. Use the `;nick` command to assign or look up
|
||||||
|
nicknames.
|
||||||
|
|
||||||
|
`;nick` with no arguments lists all nicknames; `;nick
|
||||||
|
~tasfyn-partyv` looks up a nickname; `;nick curtis` searches in
|
||||||
|
reverse; `;nick ~tasfyn-partyv curtis` creates a nickname.
|
||||||
|
All nicknames must be 14 characters or less, lowercase.
|
||||||
|
|
||||||
|
Of course, nicknames are strictly local - like the names on
|
||||||
|
entries in a phonebook. Sometimes in a post you want to mention
|
||||||
|
someone you know by a nickname. Just type `~curtis`, and `:talk`
|
||||||
|
will replace it magically with `~tasfyn-partyv` (or beep if no
|
||||||
|
`~curtis` is bound).
|
||||||
|
|
||||||
|
#### Presence
|
||||||
|
|
||||||
|
You'll see presence notifications when people enter or leave
|
||||||
|
stations you're subscribed to.
|
||||||
|
|
||||||
|
`;who` lists everyone in all your stations. `;who station`
|
||||||
|
lists everyone in that station.
|
||||||
|
|
||||||
|
#### Typing indicator
|
||||||
|
|
||||||
|
If one or more urbits in your audience is typing, `:talk`'s
|
||||||
|
presence system will detect it and change the prompt:
|
||||||
|
```
|
||||||
|
~tasfyn-partyv [~wictuc-folrex...]>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Creating and managing stations
|
||||||
|
|
||||||
|
To create your own mailbox, party, journal or board:
|
||||||
|
```
|
||||||
|
;create party %myfunparty
|
||||||
|
;create journal %serious-journal
|
||||||
|
;create board %bizarre-board
|
||||||
|
```
|
||||||
|
etc.
|
||||||
|
|
||||||
|
Every form of station has an exception list; to block
|
||||||
|
`~wictuc-folrex` from your default mailbox `%porch`,
|
||||||
|
```
|
||||||
|
;block %porch ~wictuc-folrex
|
||||||
|
```
|
||||||
|
To invite people to `%myfunparty`:
|
||||||
|
```
|
||||||
|
;invite %myfunparty ~wictuc-folrex, ~sondel-forsut
|
||||||
|
```
|
||||||
|
To ban from `%bizarre-board`:
|
||||||
|
```
|
||||||
|
;banish %bizarre-board ~wictuc-folrex
|
||||||
|
```
|
||||||
|
To appoint a coauthor of `%serious-journal`:
|
||||||
|
```
|
||||||
|
;author %serious-journal ~sondel-forsut
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Station Glyphs
|
||||||
|
|
||||||
|
Station are assigned out of the list `:|}>`, then
|
||||||
|
randomly out of it and the sets `-+*.`, ``,=`'^\/``,
|
||||||
|
`$%&@`, and `{<[]()`, in decreasing order of probabilty.
|
||||||
|
Alphanumeric characters and `!#?;~_` are reserved.
|
50
pub/doc/tools/tree.md
Normal file
50
pub/doc/tools/tree.md
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
# `:tree`
|
||||||
|
|
||||||
|
`:tree` is the web filesystem interface.
|
||||||
|
|
||||||
|
`:tree` is a single-page app that uses a backend in `/home/tree` to load contents from `%clay` as the user navigates around as `%json`. The frontend lives in `/home/pub/tree` and is a fairly straightforward [React](https://facebook.github.io/react/) / [Flux](https://facebook.github.io/flux/) app.
|
||||||
|
|
||||||
|
## Frontend
|
||||||
|
|
||||||
|
The frontend code for `:tree` can be found in `/home/pub/tree/src/`.
|
||||||
|
|
||||||
|
### CSS
|
||||||
|
|
||||||
|
The CSS is written in [Stylus](https://learnboost.github.io/stylus/). The main entry point is `main.styl` and can be compiled with `stylus main.styl` which should output a `main.css`
|
||||||
|
|
||||||
|
### JS
|
||||||
|
|
||||||
|
The JS is written in [CoffeeScript](http://coffeescript.org/) and packaged with [Browserify](http://browserify.org/). The main entry point is `main.coffee` and is compiled with `browserify -t coffeeify main.coffee > main.js`. You'll need to `npm install` first.
|
||||||
|
|
||||||
|
Each page is loaded as JSON and then rendered using React on the page. This allows us to write JSX in our markdown to implement simple components. Check out `/home/pub/tree/src/js/components` to see the component library.
|
||||||
|
|
||||||
|
You'll notice that some of these doc pages use things like `<list>` in the raw markdown files.
|
||||||
|
|
||||||
|
## JSON API
|
||||||
|
Async provides loading by schema
|
||||||
|
|
||||||
|
`{path name sein sibs next prev}` are all immediately accesible from the store
|
||||||
|
|
||||||
|
a `getPath` method, if present (defaulting to current url), is used to determine the query root node.
|
||||||
|
|
||||||
|
## JSON Internals
|
||||||
|
|
||||||
|
### `/[desk]/tree/{path}.json`
|
||||||
|
`tree/json.hook` accepts a query string schema `q` in light noun encoding
|
||||||
|
|
||||||
|
++ schema (dict ,[term $|(mark schema)])
|
||||||
|
++ dict |*(a=_,* $&([a (dict a)] a))
|
||||||
|
|
||||||
|
which is normalized and type-checked to a `query` list of
|
||||||
|
- `[%kids query]`, the only recursive value, which executes for all subpaths
|
||||||
|
XX descent is only currently supported to a single level as a performance optimization
|
||||||
|
- `[%name %t]`, the node name
|
||||||
|
- `[%path %t]`, the current path
|
||||||
|
- `[%snip %r]`, a snippet, extracted via `react-snip-json`
|
||||||
|
- `[%head %r]`, the first `<h1/>`, extracted via `react-head-json`
|
||||||
|
- `[%body %r]`, the `react-json` body
|
||||||
|
- `[%meta %j]`, json frontmatter per the `mdy` mark definition
|
||||||
|
|
||||||
|
The request types above are `%t` text, `%r` html-derived tree, and `%j`
|
||||||
|
arbitrary json; an example query, used by the main content renderer, is
|
||||||
|
`"q=body.r__kids_name.t"` (`body:'r' kids:{name:'t'}` )
|
@ -1,34 +0,0 @@
|
|||||||
# Tree
|
|
||||||
|
|
||||||
`:tree` is the web filesystem interface.
|
|
||||||
|
|
||||||
# Data retrieval interface
|
|
||||||
Async provides loading by schema
|
|
||||||
|
|
||||||
`{path name sein sibs next prev}` are all immediately accesible from the store
|
|
||||||
|
|
||||||
a `getPath` method, if present (defaulting to current url), is used to determine the query root node.
|
|
||||||
|
|
||||||
# Internals
|
|
||||||
|
|
||||||
something something coffeescript
|
|
||||||
|
|
||||||
## `/[desk]/tree/{path}.json`
|
|
||||||
tree/json.hook accepts a query string schema `q` in light noun encoding
|
|
||||||
|
|
||||||
++ schema (dict ,[term $|(mark schema)])
|
|
||||||
++ dict |*(a=_,* $&([a (dict a)] a))
|
|
||||||
|
|
||||||
which is normalized and type-checked to a `query` list of
|
|
||||||
- `[%kids query]`, the only recursive value, which executes for all subpaths
|
|
||||||
XX descent is only currently supported to a single level as a performance optimization
|
|
||||||
- `[%name %t]`, the node name
|
|
||||||
- `[%path %t]`, the current path
|
|
||||||
- `[%snip %r]`, a snippet, extracted via `react-snip-json`
|
|
||||||
- `[%head %r]`, the first `<h1/>`, extracted via `react-head-json`
|
|
||||||
- `[%body %r]`, the `react-json` body
|
|
||||||
- `[%meta %j]`, json frontmatter per the `mdy` mark definition
|
|
||||||
|
|
||||||
The request types above are `%t` text, `%r` html-derived tree, and `%j`
|
|
||||||
arbitrary json; an example query, used by the main content renderer, is
|
|
||||||
`"q=body.r__kids_name.t"` (`body:'r' kids:{name:'t'}` )
|
|
@ -37,12 +37,17 @@ module.exports = query {
|
|||||||
elem = @props.kids[item]
|
elem = @props.kids[item]
|
||||||
href = window.tree.basepath path
|
href = window.tree.basepath path
|
||||||
parts = []
|
parts = []
|
||||||
|
title = null
|
||||||
if elem.meta?.title
|
if elem.meta?.title
|
||||||
title =
|
title =
|
||||||
gn: 'h1'
|
gn: 'h1'
|
||||||
c: [elem.meta.title]
|
c: [elem.meta.title]
|
||||||
else title = elem.head
|
if elem.head.c.length > 0
|
||||||
title ||= (h1 {},item)
|
title = elem.head
|
||||||
|
if not title
|
||||||
|
title =
|
||||||
|
gn: 'h1'
|
||||||
|
c: [item]
|
||||||
parts.push title
|
parts.push title
|
||||||
unless @props.titlesOnly # redundant? this seems familiar
|
unless @props.titlesOnly # redundant? this seems familiar
|
||||||
if @props.dataPreview
|
if @props.dataPreview
|
||||||
|
@ -646,15 +646,22 @@ module.exports = query({
|
|||||||
elem = this.props.kids[item];
|
elem = this.props.kids[item];
|
||||||
href = window.tree.basepath(path);
|
href = window.tree.basepath(path);
|
||||||
parts = [];
|
parts = [];
|
||||||
|
title = null;
|
||||||
if ((ref4 = elem.meta) != null ? ref4.title : void 0) {
|
if ((ref4 = elem.meta) != null ? ref4.title : void 0) {
|
||||||
title = {
|
title = {
|
||||||
gn: 'h1',
|
gn: 'h1',
|
||||||
c: [elem.meta.title]
|
c: [elem.meta.title]
|
||||||
};
|
};
|
||||||
} else {
|
}
|
||||||
|
if (elem.head.c.length > 0) {
|
||||||
title = elem.head;
|
title = elem.head;
|
||||||
}
|
}
|
||||||
title || (title = h1({}, item));
|
if (!title) {
|
||||||
|
title = {
|
||||||
|
gn: 'h1',
|
||||||
|
c: [item]
|
||||||
|
};
|
||||||
|
}
|
||||||
parts.push(title);
|
parts.push(title);
|
||||||
if (!this.props.titlesOnly) {
|
if (!this.props.titlesOnly) {
|
||||||
if (this.props.dataPreview) {
|
if (this.props.dataPreview) {
|
||||||
|
Loading…
Reference in New Issue
Block a user