Lesson 7: The Standard Library
recordings:
Lesson 7 (US Morning) YouTube | FileBase
Lesson 7 (US Evening) YouTube | FileBase
objectives:
"Explore the Hoon standard library."
"Produce loobean expressions."
"Reorder conditional arms."
"Switch against a union with or without default."
"Reel, roll, turn a list."
"Curry, cork functions."
"Change arity of a gate."
runes:
";:"
"?|"
"?&"
"?!"
"?."
"?-"
"?+"
irregular:
"&"
"|"
"!"
":"
keypoints:
"The Hoon standard library provides many (but not all) desirable tools. (It tends to be parser-heavy.)"
"Good Hoon style weights heavier expressions to the bottom. Use ?. wutdot to discretionally control this."
"Switch statements allow you to make decisions based on possible elements of a type union (e.g. of terms)."
"Gates can be manipulated to accept different numbers of arguments, or applied across multiple values, using functional programming arms."
"Formatted text can be produced using tanks."
homework:
The Standard Library
Whenever we run code in the dojo, the subject makes available to us a standard library of operations. Much of these focus on parsing and building code—after all, that's what Hoon itself must do—but there are many other convenient functions tucked away in the Hoon subject. Some parts are formally in Arvo, some in %zuse, some in %lull, and some in Hoon proper (hoon.hoon). (Vane-specific operations are also available, although we don't need any of them quite yet.) You don't really need to know about where particular parts live yet, although naked generators as we've composed thus far can only see arms of the standard library and don't have access to some Arvo information like our or now.
The Structure of Urbit OS
Navigate to /sys in your fakezod's pier in the %base desk. Take a look at the files present here:
. ├── arvo.hoon ├── hoon.hoon ├── lull.hoon ├── vane │ ├── ames.hoon │ ├── behn.hoon │ ├── clay.hoon │ ├── dill.hoon │ ├── eyre.hoon │ ├── gall.hoon │ ├── iris.hoon │ └── jael.hoon └── zuse.hoon
Arvo and the vanes provide system services which you won't need until you start working with Gall and building apps.
hoon.hoon provides the language fundamentals for the Hoon parser and builder. (Some of this functionality is housed in the filesystem handler clay.hoon as well.)
lull.hoon contains structures and services necessary to bootstrap Arvo.
zuse.hoon provides a variety data parsing and representation structures.
arvo.hoon instruments the basic Arvo event loop.
Vane files provide per-vane service cores and molds.
We are going to venture into the standard library files to start us off today. Open these with the text editor of your choice. Let's start with hoon.hoon, the core of the standard library.
hoon.hoon
hoon.hoon is a bit of a jungle. It is a core organized into “chapters” with +| lusbar. The docs reflect this structure and give us a good glimpse of what to expect in here. Some highlights:
++add ++dec ++div ++dvr ++gte ++gth ++lte ++lth ++max ++min ++mod ++mul ++sub
++biff ++bind ++bond ++both ++clap ++clef ++drop ++fall ++flit ++hunt ++lift ++mate ++need ++some
++bake ++fand ++find ++flop ++gulf ++homo ++join ++lent ++levy ++lien ++limo ++murn ++oust ++reap ++reel ++roll ++scag ++skid ++skim ++skip ++slag ++snag ++snip ++snoc ++sort ++spin ++spun ++swag ++turn ++weld ++welp ++zing
++in ++all:in ++any:in ++apt:in ++bif:in ++del:in ++dif:in ++dig:in ++gas:in ++has:in ++int:in ++put:in ++rep:in ++run:in ++tap:in ++uni:in ++wyt:in
++by ++all:by ++any:by ++apt:by ++bif:by ++del:by ++dif:by ++dig:by ++gas:by ++get:by ++got:by ++has:by ++int:by ++jab:by ++key:by ++mar:by ++put:by ++rep:by ++rib:by ++run:by ++rut:by ++tap:by ++uni:by ++uno:by ++urn:by ++wyt:by
++aftr ++cork ++corl ++curr ++cury ++fore ++head ++same ++succ ++tail ++test ++lead ++late
+$axis +$bean +$char +$cord +$byts +$date +$flag +$knot +$noun +$path +$stud +$tang +$tank +$tape +$tour +$tarp +$term +$wain +$wall
++rs ++add:rs ++bit:rs ++div:rs ++drg:rs ++equ:rs ++exp:rs ++fma:rs ++grd:rs ++gte:rs ++gth:rs ++lte:rs ++lth:rs ++ma:rs ++mul:rs ++san:rs ++sea:rs ++sig:rs ++sqt:rs ++sub:rs ++sun:rs ++toi:rs
++yo ++cet:yo ++day:yo ++era:yo ++hor:yo ++jes:yo ++mit:yo ++moh:yo ++moy:yo ++qad:yo ++yer:yo
Urbit phonetic base (@p): ++po ++ind:po ++ins:po ++tod:po ++tos:po
Bitcoin base-58: ++fa ++cha:fa ++tok:fa ++pad:fa ++enc:fa ++den:fa
++cass ++crip ++cuss ++mesc ++runt ++sand ++sane ++teff ++trim ++trip ++tuba ++tufa ++tuft ++taft ++wack ++wick ++woad ++wood
++scot ++scow ++slat ++slav ++slaw ++slay ++smyt ++spat ++spud ++stab ++stap
Let's examine some specific implementations:
How do we convert text into all upper-case?
How do we turn a cord into a tape?
How can we make a list of a null-terminated tuple?
How can we evaluate Nock expressions?
(If you see a |* bartar rune in there, it's similar to a |= bartis, but what's called a wet gate.)
zuse.hoon
When you open zuse.hoon, you'll see that it is composed with some data structures from %lull, but that by and large it consists of a core including arms organized into “engines”.
Most of these are internal Arvo conventions, such as conversion between Unix-epoch times and Urbit-epoch times. The main one you are likely to work with is the ++html core, which contains important tools for working with web-based data, such as MIME types and JSON strings.
To convert a @ux hexadecimal value to a cord:
(en:base16:mimes:html [3 0x12.3456]) '123456'
To convert a cord to a @ux hexadecimal value:
@uxq.+>:(de:base16:mimes:html '123456') 0x12.3456
There are tools for working with Bitcoin wallet base-58 values, JSON strings, XML strings, and more.
(en-urlt:html "https://hello.me") "https%3A%2F%2Fhello.me"
What seems to be missing from the standard library?
Conditional Expressions
Let's wrap up the ? wut runes, many of which you have already seen:
Conditional decision-making:
?: wutcol lets you branch between an expression-if-true and an expression-if-false.
?. wutdot inverts the order of ?:. Good Hoon style prescribes that the heavier branch of a logical expression should be lower in the file.
?- wuthep lets you choose between several possibilities, as with a type union. Every case must be handled and no case can be unreachable.
?+ wutlus is similar to ?- but allows a default value in case no branch is taken.
Assertions:
?> wutgar is a positive assertion (%.y% or crash).
?< wutgal is a negative assertion (%.n or crash).
?~ wutsig asserts non-null.
?^ wutket asserts cell.
?@ wutpat asserts atom.
Logical operators:
?& wutpam, irregularly &(), is a logical AND over loobean values.
?| wutbar, irregularly |(), is a logical OR over loobean values.
?! wutzap, irregularly !, is a logical NOT.
Pattern matching:
- ?= wuttis tests for a pattern match in type, someday to be superseded or supplemented by a planned ?# wuthax rune.
Functional Programming
Given a gate, you can manipulate it to accept a different number of values than its sample formally requires, or otherwise modify its behavior.
Changing Arity
If a gate accepts only two values in its sample, for instance, you can chain together multiple calls automatically using ;: miccol:
(add 3 (add 4 5)) 12 :(add 3 4 5) 12 (mul 3 (mul 4 5)) 60 :(mul 3 4 5) 60
This is called changing the arity of the gate. (Does this work on ++mul:rs?)
Binding the Sample
If you have a gate which accepts multiple values in the sample, you can fix one of these. To fix the head of the sample (the first argument), use ++cury; to bind the tail, use ++curr.
Consider calculating a x² + b x + c, a situation we earlier resolved using a door. We can resolve the situation differently using currying:
=full |=([x=@ud a=@ud b=@ud c=@ud] (add (add (mul (mul x x) a) (mul x b)) c)) (full 5 4 3 2) 117 =one (curr full [4 3 2]) (one 5) 117
One can also ++cork a gate, or arrange it such that it applies to the result of the next gate. This pairs well with ;: miccol. (There is also ++corl.) This example converts a value to @ux then decrements it:
((cork dec @ux) 20) 0x13
Reeling A Jig
++roll and ++reel are used to left-fold and right-fold a list, respectively. To fold a list is similar to ++turn, except that instead of yielding a list with the values having had each applied, ++roll and ++reel produce an accumulated value.
(roll
(list @)[1 2 3 4 5 ~] add) q=15 (reel(list @)[1 2 3 4 5 ~] mul) 120
Formatted Text
A +$tank is a formatted print tree. Error messages and the like are built of tanks. They are defined in hoon.hoon:
:: tank @ cord tang (list tank) :: bottom-first error
The ++ram:re arm is used to convert these to actual formatted output, e.g.
~(ram re leaf+"foo") "foo" ~(ram re [%palm ["|" "(" "!" ")"] leaf+"foo" leaf+"bar" leaf+"baz" ~]) "(!foo|bar|baz)" ~(ram re [%rose [" " "[" "]"] leaf+"foo" leaf+"bar" leaf+"baz" ~]) "[foo bar baz]"
Many generators build sophisticated output using tanks and the short-format builder +, e.g. in /gen/azimuth-block/hoon:
[leaf+(scow %ud block)]~
At this point we aren't going to use tanks directly yet, but you'll see them more and more as you move into more advanced generators.
Deep Dive: cat.hoon
For instance, how does +cat work? Let's look at the structure of /gen/cat/hoon:
:: ConCATenate file listings
::
:::: /hoon/cat/gen
::
/? 310
/+ pretty-file, show-dir
::
::::
::
:- %say
|= [^ [arg=(list path)] vane=?(%g %c)]
=- tang+(flop tang(zing -))
%+ turn arg
|= pax=path
^- tang
=+ ark=.^(arch (cat 3 vane %y) pax)
?^ fil.ark
?: =(%sched -:(flop pax))
[>.^((map @da cord) (cat 3 vane %x) pax)<]~
[leaf+(spud pax) (pretty-file .^(noun (cat 3 vane %x) pax))]
?- dir.ark :: handle ambiguity
~
[rose+[" " ~]^~[leaf+"~" (smyt pax)]]~ :: [[@t ~] ~ ~] $(pax (welp pax /[p.n.dir.ark])) :: * =- [palm+[": " ``~]^-]~ :~ rose+[" " ]^[leaf+"*" (smyt pax)]
tank(show-dir vane pax dir.ark)
==
==
What is the top-level structure of the generator? (A cell of %say and the gate, previewing %say generators.)
What don't you recognize?
/? faswut pins the expected Arvo kelvin version; right now it doesn't do anything
.^ dotket loads a value from Arvo (called a “scry”)
++smyt pretty-prints a path
=- tishep combines with the subject, inverted relative to =+/=/.
There are some tape interpolation and list construction tools we haven't used yet:
[<56>] "56" [<56>]~ ["56" ~] ~[<56>] ["56" ~] /[4] [4 ~]
?- wuthep we saw up above: it's a switch statement.