From cd59a0481787ff3462dc711376c224ea12c859a4 Mon Sep 17 00:00:00 2001 From: Philip C Monk Date: Fri, 25 Sep 2015 13:37:25 -0400 Subject: [PATCH 1/4] added cancel --- gen/hood/cancel.hoon | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 gen/hood/cancel.hoon diff --git a/gen/hood/cancel.hoon b/gen/hood/cancel.hoon new file mode 100644 index 0000000000..831d7469c3 --- /dev/null +++ b/gen/hood/cancel.hoon @@ -0,0 +1,13 @@ +:: +:::: /hoon/cancel/hood/gen + :: +/? 314 +:: +:::: + !: +:- %say +|= $: [now=@da eny=@uvI bec=beak] + [[syd=@tas ~] ~] + == +:- %kiln-cancel +syd From 6651e78509bc4e2acf306308eb45554f9706735f Mon Sep 17 00:00:00 2001 From: Galen Wolfe-Pauly Date: Tue, 29 Sep 2015 14:12:57 -0700 Subject: [PATCH 2/4] doc reorg and clean, tree blank item fix --- gen/hood/begin.hoon | 2 +- pub/doc.md | 10 +- pub/doc/{arvo.md => arvo.mdy} | 4 + pub/doc/arvo/ives.md | 8 - pub/doc/arvo/ives/ives.md | 5 - pub/doc/arvo/jael.md | 8 - pub/doc/arvo/jael/jael.md | 5 - pub/doc/arvo/kahn.md | 8 - pub/doc/arvo/kahn/kahn.md | 5 - pub/doc/arvo/lunt.md | 8 - pub/doc/arvo/lunt/lunt.md | 5 - pub/doc/{hoon.md => hoon.mdy} | 4 + pub/doc/hoon/reference/pronunciation.md | 68 +++ pub/doc/{reference.md => interpreter.mdy} | 8 +- .../{reference => interpreter}/glossary.md | 0 pub/doc/{reference => interpreter}/u3.md | 0 pub/doc/{reference => interpreter}/vere.md | 0 pub/doc/{nock.md => nock.mdy} | 4 + pub/doc/talk.md | 14 - pub/doc/talk/help.txt | 24 - pub/doc/tools.mdy | 10 + pub/doc/tools/clay.md | 207 +++++++ pub/doc/tools/dojo.md | 548 ++++++++++++++++++ pub/doc/tools/talk.md | 287 +++++++++ pub/doc/tools/tree.md | 50 ++ pub/doc/tree.md | 34 -- .../src/js/components/ListComponent.coffee | 9 +- pub/tree/src/js/main.js | 11 +- 28 files changed, 1209 insertions(+), 137 deletions(-) rename pub/doc/{arvo.md => arvo.mdy} (99%) delete mode 100644 pub/doc/arvo/ives.md delete mode 100644 pub/doc/arvo/ives/ives.md delete mode 100644 pub/doc/arvo/jael.md delete mode 100644 pub/doc/arvo/jael/jael.md delete mode 100644 pub/doc/arvo/kahn.md delete mode 100644 pub/doc/arvo/kahn/kahn.md delete mode 100644 pub/doc/arvo/lunt.md delete mode 100644 pub/doc/arvo/lunt/lunt.md rename pub/doc/{hoon.md => hoon.mdy} (97%) create mode 100644 pub/doc/hoon/reference/pronunciation.md rename pub/doc/{reference.md => interpreter.mdy} (63%) rename pub/doc/{reference => interpreter}/glossary.md (100%) rename pub/doc/{reference => interpreter}/u3.md (100%) rename pub/doc/{reference => interpreter}/vere.md (100%) rename pub/doc/{nock.md => nock.mdy} (98%) delete mode 100644 pub/doc/talk.md delete mode 100644 pub/doc/talk/help.txt create mode 100644 pub/doc/tools.mdy create mode 100644 pub/doc/tools/clay.md create mode 100644 pub/doc/tools/dojo.md create mode 100644 pub/doc/tools/talk.md create mode 100644 pub/doc/tools/tree.md delete mode 100644 pub/doc/tree.md diff --git a/gen/hood/begin.hoon b/gen/hood/begin.hoon index efc0ae5fb6..301bda1cb2 100644 --- a/gen/hood/begin.hoon +++ b/gen/hood/begin.hoon @@ -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 diff --git a/pub/doc.md b/pub/doc.md index 94b4ab1ace..1f0a366ca7 100644 --- a/pub/doc.md +++ b/pub/doc.md @@ -1,7 +1,7 @@ -urbit -===== +Urbit Manual +============ -is a general-purpose computing stack designed to live in the cloud. +Urbit is a general-purpose computing stack designed to live in the cloud. @@ -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. diff --git a/pub/doc/arvo.md b/pub/doc/arvo.mdy similarity index 99% rename from pub/doc/arvo.md rename to pub/doc/arvo.mdy index 961231efc0..3ca61fa60e 100644 --- a/pub/doc/arvo.md +++ b/pub/doc/arvo.mdy @@ -1,3 +1,7 @@ +--- +sort: 2 +--- + arvo ==== diff --git a/pub/doc/arvo/ives.md b/pub/doc/arvo/ives.md deleted file mode 100644 index 5eb8907d98..0000000000 --- a/pub/doc/arvo/ives.md +++ /dev/null @@ -1,8 +0,0 @@ -
- -`%ives` -======= - -Isn't finished yet. - -
diff --git a/pub/doc/arvo/ives/ives.md b/pub/doc/arvo/ives/ives.md deleted file mode 100644 index 48016505c8..0000000000 --- a/pub/doc/arvo/ives/ives.md +++ /dev/null @@ -1,5 +0,0 @@ -Ives: Reference -=============== - -Ives: Commentary -================ diff --git a/pub/doc/arvo/jael.md b/pub/doc/arvo/jael.md deleted file mode 100644 index ab06b5373b..0000000000 --- a/pub/doc/arvo/jael.md +++ /dev/null @@ -1,8 +0,0 @@ -
- -`%jael` -======= - -Isn't finished yet. - -
diff --git a/pub/doc/arvo/jael/jael.md b/pub/doc/arvo/jael/jael.md deleted file mode 100644 index 655d98e64b..0000000000 --- a/pub/doc/arvo/jael/jael.md +++ /dev/null @@ -1,5 +0,0 @@ -Jael: Reference -=============== - -Jael: Commentary -================ diff --git a/pub/doc/arvo/kahn.md b/pub/doc/arvo/kahn.md deleted file mode 100644 index 744ffae6d6..0000000000 --- a/pub/doc/arvo/kahn.md +++ /dev/null @@ -1,8 +0,0 @@ -
- -`%kahn` -======= - -Isn't finished yet. - -
diff --git a/pub/doc/arvo/kahn/kahn.md b/pub/doc/arvo/kahn/kahn.md deleted file mode 100644 index 8096fe83a3..0000000000 --- a/pub/doc/arvo/kahn/kahn.md +++ /dev/null @@ -1,5 +0,0 @@ -Kahn: Reference -=============== - -Kahn: Commentary -================ diff --git a/pub/doc/arvo/lunt.md b/pub/doc/arvo/lunt.md deleted file mode 100644 index 46f6f60dce..0000000000 --- a/pub/doc/arvo/lunt.md +++ /dev/null @@ -1,8 +0,0 @@ -
- -`%lunt` -======= - -Isn't finished yet. - -
diff --git a/pub/doc/arvo/lunt/lunt.md b/pub/doc/arvo/lunt/lunt.md deleted file mode 100644 index ba19477bc2..0000000000 --- a/pub/doc/arvo/lunt/lunt.md +++ /dev/null @@ -1,5 +0,0 @@ -Lunt: Reference -=============== - -Lunt: Commentary -================ diff --git a/pub/doc/hoon.md b/pub/doc/hoon.mdy similarity index 97% rename from pub/doc/hoon.md rename to pub/doc/hoon.mdy index 7655a426ce..c0dfeabf98 100644 --- a/pub/doc/hoon.md +++ b/pub/doc/hoon.mdy @@ -1,3 +1,7 @@ +--- +sort: 1 +--- +
hoon diff --git a/pub/doc/hoon/reference/pronunciation.md b/pub/doc/hoon/reference/pronunciation.md new file mode 100644 index 0000000000..f7b4fab9af --- /dev/null +++ b/pub/doc/hoon/reference/pronunciation.md @@ -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 diff --git a/pub/doc/reference.md b/pub/doc/interpreter.mdy similarity index 63% rename from pub/doc/reference.md rename to pub/doc/interpreter.mdy index 5e85776d8d..4c0f3d951f 100644 --- a/pub/doc/reference.md +++ b/pub/doc/interpreter.mdy @@ -1,9 +1,13 @@ +--- +sort: 3 +--- +
-References +Interpreter ========== -These references cover terminology and our interpreter, `vere`. +The urbit interpreter and C code.
diff --git a/pub/doc/reference/glossary.md b/pub/doc/interpreter/glossary.md similarity index 100% rename from pub/doc/reference/glossary.md rename to pub/doc/interpreter/glossary.md diff --git a/pub/doc/reference/u3.md b/pub/doc/interpreter/u3.md similarity index 100% rename from pub/doc/reference/u3.md rename to pub/doc/interpreter/u3.md diff --git a/pub/doc/reference/vere.md b/pub/doc/interpreter/vere.md similarity index 100% rename from pub/doc/reference/vere.md rename to pub/doc/interpreter/vere.md diff --git a/pub/doc/nock.md b/pub/doc/nock.mdy similarity index 98% rename from pub/doc/nock.md rename to pub/doc/nock.mdy index aa9a8f8974..c477a997f1 100644 --- a/pub/doc/nock.md +++ b/pub/doc/nock.mdy @@ -1,3 +1,7 @@ +--- +sort: 0 +--- +
nock diff --git a/pub/doc/talk.md b/pub/doc/talk.md deleted file mode 100644 index b2ac5e66b4..0000000000 --- a/pub/doc/talk.md +++ /dev/null @@ -1,14 +0,0 @@ -
- -talk -==== - -talk is our messaging application. - -talk is in dire need of better documentation - -
- ------------------------------------------------------------------------- - -This document should include the command reference somehow. diff --git a/pub/doc/talk/help.txt b/pub/doc/talk/help.txt deleted file mode 100644 index eaf20e62a4..0000000000 --- a/pub/doc/talk/help.txt +++ /dev/null @@ -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. - - ; - -`;` activates a previous message number, like a URL that got -clipped. - - ; - -`;` 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. diff --git a/pub/doc/tools.mdy b/pub/doc/tools.mdy new file mode 100644 index 0000000000..a1b0b7cafa --- /dev/null +++ b/pub/doc/tools.mdy @@ -0,0 +1,10 @@ +--- +sort: 4 +--- + +tools +==== + +User-level tools and utilities. + + diff --git a/pub/doc/tools/clay.md b/pub/doc/tools/clay.md new file mode 100644 index 0000000000..b48d94ad9d --- /dev/null +++ b/pub/doc/tools/clay.md @@ -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 +` `. 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 \ No newline at end of file diff --git a/pub/doc/tools/dojo.md b/pub/doc/tools/dojo.md new file mode 100644 index 0000000000..c273c78ba4 --- /dev/null +++ b/pub/doc/tools/dojo.md @@ -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`. diff --git a/pub/doc/tools/talk.md b/pub/doc/tools/talk.md new file mode 100644 index 0000000000..04656e1a77 --- /dev/null +++ b/pub/doc/tools/talk.md @@ -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. diff --git a/pub/doc/tools/tree.md b/pub/doc/tools/tree.md new file mode 100644 index 0000000000..e170b2eda3 --- /dev/null +++ b/pub/doc/tools/tree.md @@ -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 `` 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 `

`, 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'}` ) diff --git a/pub/doc/tree.md b/pub/doc/tree.md deleted file mode 100644 index fa6f071f04..0000000000 --- a/pub/doc/tree.md +++ /dev/null @@ -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 `

`, 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'}` ) diff --git a/pub/tree/src/js/components/ListComponent.coffee b/pub/tree/src/js/components/ListComponent.coffee index 72b3784f8f..8aead25033 100644 --- a/pub/tree/src/js/components/ListComponent.coffee +++ b/pub/tree/src/js/components/ListComponent.coffee @@ -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 diff --git a/pub/tree/src/js/main.js b/pub/tree/src/js/main.js index d7ae996c2f..16b13a605f 100644 --- a/pub/tree/src/js/main.js +++ b/pub/tree/src/js/main.js @@ -634,15 +634,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) { From 0cc523538aee85a0abfe7a6153a1e9c6f98f3166 Mon Sep 17 00:00:00 2001 From: Galen Wolfe-Pauly Date: Tue, 29 Sep 2015 14:29:38 -0700 Subject: [PATCH 3/4] based css --- lib/base.css | 605 +++++++-------------------------------------------- 1 file changed, 83 insertions(+), 522 deletions(-) diff --git a/lib/base.css b/lib/base.css index 6a5cd5955b..94fae6346f 100644 --- a/lib/base.css +++ b/lib/base.css @@ -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; } } \ No newline at end of file From e97bec0bb58a35299bd6ab6d52f2ae65177b9cbb Mon Sep 17 00:00:00 2001 From: Galen Wolfe-Pauly Date: Tue, 29 Sep 2015 14:36:58 -0700 Subject: [PATCH 4/4] tutorial drafts --- pub/doc/hoon/tutorial.md | 4 + pub/doc/hoon/tutorial/0-nouns.md | 232 ++++++++++++++++ pub/doc/hoon/tutorial/1-twigs.md | 229 ++++++++++++++++ pub/doc/hoon/tutorial/2-syntax.md | 392 +++++++++++++++++++++++++++ pub/doc/hoon/tutorial/3-program.md | 316 +++++++++++++++++++++ pub/doc/hoon/tutorial/4-functions.md | 261 ++++++++++++++++++ 6 files changed, 1434 insertions(+) create mode 100644 pub/doc/hoon/tutorial.md create mode 100644 pub/doc/hoon/tutorial/0-nouns.md create mode 100644 pub/doc/hoon/tutorial/1-twigs.md create mode 100644 pub/doc/hoon/tutorial/2-syntax.md create mode 100644 pub/doc/hoon/tutorial/3-program.md create mode 100644 pub/doc/hoon/tutorial/4-functions.md diff --git a/pub/doc/hoon/tutorial.md b/pub/doc/hoon/tutorial.md new file mode 100644 index 0000000000..1048ccb962 --- /dev/null +++ b/pub/doc/hoon/tutorial.md @@ -0,0 +1,4 @@ +Tutorials +========= + + \ No newline at end of file diff --git a/pub/doc/hoon/tutorial/0-nouns.md b/pub/doc/hoon/tutorial/0-nouns.md new file mode 100644 index 0000000000..3dec5abd41 --- /dev/null +++ b/pub/doc/hoon/tutorial/0-nouns.md @@ -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. diff --git a/pub/doc/hoon/tutorial/1-twigs.md b/pub/doc/hoon/tutorial/1-twigs.md new file mode 100644 index 0000000000..9ae966536a --- /dev/null +++ b/pub/doc/hoon/tutorial/1-twigs.md @@ -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. diff --git a/pub/doc/hoon/tutorial/2-syntax.md b/pub/doc/hoon/tutorial/2-syntax.md new file mode 100644 index 0000000000..3864ba48c9 --- /dev/null +++ b/pub/doc/hoon/tutorial/2-syntax.md @@ -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. diff --git a/pub/doc/hoon/tutorial/3-program.md b/pub/doc/hoon/tutorial/3-program.md new file mode 100644 index 0000000000..964e62f160 --- /dev/null +++ b/pub/doc/hoon/tutorial/3-program.md @@ -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? diff --git a/pub/doc/hoon/tutorial/4-functions.md b/pub/doc/hoon/tutorial/4-functions.md new file mode 100644 index 0000000000..c8429e3d60 --- /dev/null +++ b/pub/doc/hoon/tutorial/4-functions.md @@ -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)`