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