Merge branch 'master' of https://github.com/urbit/urbit into nobreach

This commit is contained in:
Anton Dyudin 2015-10-02 13:10:28 -07:00
commit e40b0ab467
36 changed files with 2739 additions and 659 deletions

View File

@ -21,7 +21,7 @@
?~ +.arg -
(fun.q.q tic.arg)
%+ sole-lo
[%& %helm-begin "your ship: ~"]
[%& %helm-begin "your urbit: ~"]
%+ sole-go fed:ag
|= his=@p
%+ sole-lo

13
gen/hood/cancel.hoon Normal file
View File

@ -0,0 +1,13 @@
::
:::: /hoon/cancel/hood/gen
::
/? 314
::
::::
!:
:- %say
|= $: [now=@da eny=@uvI bec=beak]
[[syd=@tas ~] ~]
==
:- %kiln-cancel
syd

View File

@ -70,552 +70,113 @@
font-weight: 500;
font-style: normal;
}
body,
html {
font-family: "bau", "Helvetica Neue", helvetica, arial, sans-serif;
@font-face {
font-family: "scp";
src: url("//storage.googleapis.com/urbit-extra/scp-bold.woff");
font-weight: 600;
font-style: normal;
}
code,
pre,
li:before,
.spin,
#bred a,
h3.time {
font-family: "scp", "Courier New", courier, monospace;
@font-face {
font-family: "scp";
src: url("//storage.googleapis.com/urbit-extra/scp-black.woff");
font-weight: 700;
font-style: normal;
}
body,
html {
font-size: 18px;
font-weight: 400;
line-height: 1.6rem;
-webkit-text-size-adjust: none;
}
a {
color: #000;
text-decoration: none;
border-bottom: 2px solid #000;
display: inline-block;
line-height: 0.8rem;
}
hr {
display: inline-block;
width: 6rem;
border: 0;
border-top: 2px solid #f4f4f4;
}
h1 {
margin-top: 4rem;
}
h2,
h3 {
html,
body {
margin: 0;
margin-top: 2rem;
}
h1,
h2,
h3,
h4,
strong {
font-weight: 500;
}
h4 {
margin-bottom: 0.3rem;
}
h5 {
font-style: italic;
font-weight: 200;
margin: 0;
}
h1 code,
h2 code,
h3 code {
font-size: inherit;
padding: 0.3rem;
}
pre,
code {
font-size: 0.8rem;
}
pre {
background-color: #f5f5f5;
padding: 0.3rem;
margin-left: -0.3rem;
}
code {
line-height: 1.2rem;
background-color: #f4f4f4;
margin-top: -0.05rem;
padding: 0.2rem;
display: inline-block;
}
ul {
list-style: none;
padding: 0;
}
li:before {
content: "+";
padding-right: 0.3rem;
font-size: 0.8rem;
font-weight: 600;
html,
input,
button,
body {
font-family: "bau";
font-size: 18px;
}
#nav,
#cont {
pre,
code,
.mono {
font-family:"scp";
}
#c {
width: 32rem;
margin-left: -16rem;
position: absolute;
left: 50%;
}
#cont {
width: 42rem;
margin-left: -21rem;
background-color: #fff;
z-index: 1;
}
#nav {
position: fixed;
top: 0rem;
width: 57rem;
padding-top: 1rem;
z-index: 0;
margin-left: -32rem;
overflow: hidden;
opacity: 0;
transition: opacity 1s ease-in-out;
}
#nav.moving {
opacity: 1;
transition: opacity 0.3s ease-in-out;
}
#nav:hover {
opacity: 1;
transition: opacity 0.3s ease-in-out;
}
#cont {
position: absolute;
top: 0;
margin-bottom: 9rem;
}
.loading {
display: inline-block;
}
.spin {
color: #fff;
padding: 0.6rem;
font-size: 0.7rem;
font-weight: 600;
letter-spacing: 0.1rem;
z-index: 3;
}
.loading > .spin {
background-color: #555;
}
#body .loading > .spin {
background-color: #000;
}
.spin.state-0:before {
content: "\2599";
}
.spin.state-1:before {
content: "\259B";
}
.spin.state-2:before {
content: "\259C";
}
.spin.state-3:before {
content: "\259F";
}
#load.load {
display: inline-block;
font-weight: 500;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(10,10,10,0.4);
opacity: 1;
transition: opacity 1s ease-in-out;
z-index: 4;
}
img.logo {
height: 2rem;
width: 2rem;
}
h3.time {
margin-top: 0.3rem;
font-size: 0.7rem;
font-weight: 200;
}
#nav .links > div {
display: inline-block;
vertical-align: top;
}
#nav #sibs {
width: 8rem;
transition: margin-top 0.3s ease-in-out;
overflow: hidden;
}
#nav #sibs > div {
height: 20px;
margin-bottom: 4px;
}
.focus #sibs {
margin-top: 0 !important;
transition: margin-top 0.3s ease-in-out;
}
#nav a,
.list > li > a {
text-transform: uppercase;
font-size: 0.7rem;
font-weight: 200;
letter-spacing: 1px;
white-space: nowrap;
}
.list > li > a {
border-bottom: none;
margin-bottom: 0.3rem;
}
#nav a,
.list > li > a h1 {
border-bottom: 1px solid #000;
margin-right: 0.3rem;
}
#nav .active a {
font-weight: 500;
text-decoration: none;
}
#up {
padding-right: 1rem;
margin-top: -0.3rem;
}
#sides {
float: right;
}
#sides a {
margin-right: 0.6rem;
}
#nav .arow-up,
#nav .arow-next,
#nav .arow-prev {
width: 0;
height: 0;
border: 0.4rem solid transparent;
}
#nav .arow-up {
border-bottom: 0.6rem solid #000;
}
#nav .arow-next {
border-left: 0.6rem solid #000;
}
#nav .arow-prev {
border-right: 0.6rem solid #000;
}
#bred {
width: 5rem;
padding-right: 1rem;
text-align: right;
font-size: 0.6rem;
white-space: nowrap;
overflow: hidden;
}
#bred a {
text-transform: lowercase;
vertical-align: top;
}
#bred > div {
float: right;
}
#bred > div > div {
display: inline-block;
margin-top: -0.2rem;
}
#bred a,
#kids a {
margin-right: 0.3rem;
}
#bred a {
margin-left: 0.3rem;
}
.short {
width: 32rem;
}
.list h1,
.list li a > div p {
margin: 0;
}
.list li a > div,
.list li a > div p {
display: inline;
}
.list li a > div p {
margin-left: 0.3rem;
}
.list li a > div p:first-child {
margin-left: 0;
}
.list li a > div p code {
font-size: 0.7rem;
font-weight: 400;
text-transform: none;
}
.list li a h1 code {
text-transform: lowercase;
border-bottom: 1px solid #000;
}
.list li a code {
padding: 0.2rem;
}
.list h1,
.list li a > div div {
display: inline;
}
.list li a > div div {
margin-left: 0.6rem;
overflow: hidden;
}
.list h1 {
font-size: 0.7rem;
}
.list.posts .post {
margin-bottom: 2rem;
}
.list.posts .post h1 {
text-transform: none;
h1 {
font-size: 1.6rem;
line-height: 1.8rem;
margin-bottom: 1rem;
display: block;
}
.list.posts .post h2 {
font-size: 0.7rem;
font-weight: 400;
line-height: 1rem;
margin-top: 0;
}
.list.posts li.post:before {
content: "";
}
div.root h1 {
margin-bottom: 2rem;
}
div.root .list .sub {
margin-left: 0;
margin-right: 0.6rem;
}
div.root > p {
width: 27rem;
margin-top: 2rem;
}
h2.sub {
font-size: 0.7rem;
font-weight: 400;
line-height: 1rem;
letter-spacing: 1px;
margin-top: 0;
text-transform: uppercase;
}
div.post h1 {
font-size: 2.8rem;
line-height: 4rem;
display: block;
margin-top: 1rem;
margin-bottom: 1rem;
}
div.post h2 {
line-height: 1rem;
letter-spacing: 1px;
}
div.post h2 {
margin-top: 4rem;
}
div.post h2 {
font-size: 1.2rem;
font-weight: 500;
}
div.post p {
font-size: 1.2rem;
line-height: 2.2rem;
}
div.post li p {
display: inline;
}
div.toc {
margin-top: 3rem;
margin-bottom: 3rem;
}
div.toc h1,
div.toc h2,
div.toc h3,
div.toc h4 {
font-weight: 400;
cursor: pointer;
text-decoration: underline;
font-size: 1.2rem;
margin-top: 0.3rem;
margin-bottom: 0.3rem;
}
div.toc h2 {
h1:after {
content: "\2014";
margin-left: 1rem;
}
div.toc h3 {
margin-left: 2rem;
#c pre {
font-size: .6rem;
margin-top: 2rem;
}
div.toc h4 {
margin-left: 3rem;
#pass {
width: 32rem;
}
div.toc h1.t {
font-weight: 500;
font-size: 2rem;
text-decoration: none;
margin-bottom: 2rem;
}
#body .CodeMirror {
font-size: 0.8rem;
line-height: 1rem;
}
#body .CodeMirror .cm-header {
font-weight: 200;
}
#body .CodeMirror-gutters {
button {
border: .3rem solid #000;
background-color: #fff;
padding-right: 1rem;
margin-left: -1rem;
}
.error {
color: #f91733;
}
.warning {
background-color: #ff3537;
padding: 1rem;
width: 18rem;
margin: 2rem 0;
color: #fff;
}
.warning a {
color: inherit;
border-color: #fff;
}
.warning h1 {
font-size: 1rem;
padding: .3rem;
font-weight: 500;
}
.warning h1,
.warning p {
margin: 0 0.3rem;
.sig {
font-weight: 400;
font-size: 2rem;
display: inline;
vertical-align: middle;
}
.warning.w {
width: auto;
span#ship {
font-family: 'bau';
font-weight: 400;
font-size: 1.2rem;
text-transform: uppercase;
letter-spacing: .1rem;
display: inline-block;
min-width: 1rem;
}
@media only screen and (max-width: 1170px) {
#nav,
#nav > div,
#nav.up,
#nav.top,
#nav > .focus {
transform: translate3d(0, 0, 0);
-webkit-transform: translate3d(0, 0, 0);
}
#nav {
position: fixed;
top: 0;
opacity: 1;
width: 42rem;
margin-left: -21rem;
background-color: #fff;
z-index: 2;
}
#nav.m-down,
#nav.m-up {
position: absolute;
}
#nav.m-down.m-fixed {
position: fixed;
top: 0;
}
#nav > div {
max-height: 1rem;
overflow: hidden;
transition: max-height 0.3s ease-in-out;
}
#nav > .focus {
max-height: 40rem;
transition: max-height 0.3s ease-in-out;
}
#cont {
top: 3rem;
}
input {
font-family: 'scp';
display: inline;
}
@media only screen and (min-width: 320px) and (max-width: 1024px) {
body,
html {
font-size: 21px;
}
#nav,
#cont {
width: 94%;
padding-left: 3%;
margin-left: 0;
}
#nav {
position: fixed;
padding-top: 0;
opacity: 1;
left: 0;
background-color: #fff;
z-index: 2;
}
#nav > div {
max-height: 1.4rem;
}
#nav > div {
padding-top: 0.6rem;
}
#nav #sibs {
width: 18rem;
}
#nav #sibs > div {
height: 20px;
line-height: 20px;
}
#nav a {
display: inline-block;
font-size: 0.7rem;
}
#nav #sides {
float: right;
}
#nav .arow-up,
#nav .arow-next,
#nav .arow-prev {
margin-right: 0;
border: 0.4rem solid transparent;
}
#nav .arow-up {
border-bottom: 0.6rem solid #000;
}
#nav .arow-next {
border-left: 0.6rem solid #000;
}
#nav .arow-prev {
margin-right: 1rem;
border-right: 0.6rem solid #000;
}
#cont {
top: 3rem;
left: 0;
padding-bottom: 9rem;
}
#cont h1:first-child {
margin-top: 0;
}
.short {
width: 100%;
}
span#ship,
input {
border: none;
padding: .3rem;
outline: none;
border-bottom: 3px solid #555;
}
@media only screen and (min-device-width: 320px) and (max-device-width: 480px) {
#nav > div {
max-height: 1.6rem;
#c {
width: 16rem;
margin-left: -8rem;
}
#nav a {
font-size: 0.7rem;
#pass {
width: 16rem;
}
#nav #sibs > div {
height: 20px;
line-height: 20px;
input {
-webkit-appearance: none;
border-radius: 0;
}
}

View File

@ -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>
@ -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
[guides](doc/guide) to get oriented. Come join us on `:talk` in the
`/urbit-meta` channel to ask questions and get help.
Come join us on `:talk` in the `/urbit-meta` channel to ask questions and get help.

View File

@ -1,3 +1,7 @@
---
sort: 2
---
arvo
====

View File

@ -1,8 +0,0 @@
<div class="short">
`%ives`
=======
Isn't finished yet.
</div>

View File

@ -1,5 +0,0 @@
Ives: Reference
===============
Ives: Commentary
================

View File

@ -1,8 +0,0 @@
<div class="short">
`%jael`
=======
Isn't finished yet.
</div>

View File

@ -1,5 +0,0 @@
Jael: Reference
===============
Jael: Commentary
================

View File

@ -1,8 +0,0 @@
<div class="short">
`%kahn`
=======
Isn't finished yet.
</div>

View File

@ -1,5 +0,0 @@
Kahn: Reference
===============
Kahn: Commentary
================

View File

@ -1,8 +0,0 @@
<div class="short">
`%lunt`
=======
Isn't finished yet.
</div>

View File

@ -1,5 +0,0 @@
Lunt: Reference
===============
Lunt: Commentary
================

View File

@ -1,3 +1,7 @@
---
sort: 1
---
<div class="short">
hoon

View 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
View File

@ -0,0 +1,4 @@
Tutorials
=========
<list dataPreview="true" titlesOnly="true"></list>

View 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.

View 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.

View 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.

View 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?

View 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)`

View File

@ -1,9 +1,13 @@
---
sort: 3
---
<div class="short">
References
Interpreter
==========
These references cover terminology and our interpreter, `vere`.
The urbit interpreter and C code.
</div>

View File

@ -1,3 +1,7 @@
---
sort: 0
---
<div class="short">
nock

View File

@ -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.

View File

@ -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
View File

@ -0,0 +1,10 @@
---
sort: 4
---
tools
====
User-level tools and utilities.
<list></list>

207
pub/doc/tools/clay.md Normal file
View 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
View 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
View 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
View 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'}` )

View File

@ -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'}` )

View File

@ -37,12 +37,17 @@ module.exports = query {
elem = @props.kids[item]
href = window.tree.basepath path
parts = []
title = null
if elem.meta?.title
title =
gn: 'h1'
c: [elem.meta.title]
else title = elem.head
title ||= (h1 {},item)
if elem.head.c.length > 0
title = elem.head
if not title
title =
gn: 'h1'
c: [item]
parts.push title
unless @props.titlesOnly # redundant? this seems familiar
if @props.dataPreview

View File

@ -646,15 +646,22 @@ module.exports = query({
elem = this.props.kids[item];
href = window.tree.basepath(path);
parts = [];
title = null;
if ((ref4 = elem.meta) != null ? ref4.title : void 0) {
title = {
gn: 'h1',
c: [elem.meta.title]
};
} else {
}
if (elem.head.c.length > 0) {
title = elem.head;
}
title || (title = h1({}, item));
if (!title) {
title = {
gn: 'h1',
c: [item]
};
}
parts.push(title);
if (!this.props.titlesOnly) {
if (this.props.dataPreview) {