Skip to content

Instantly share code, notes, and snippets.

@sigilante
Created June 20, 2023 15:20
Show Gist options
  • Save sigilante/57109dafa344631f55d72bf5c7fd2fcd to your computer and use it in GitHub Desktop.
Save sigilante/57109dafa344631f55d72bf5c7fd2fcd to your computer and use it in GitHub Desktop.
Hoon Prime: A Discipline for Userspace Code

Code accessibility cannot be an afterthought for Urbit development. Hoon has had a reputation for being a language of dense expressions, which we believe is now being cleared by sunshine. However, Hoon's extensive sugar syntax and irregular expressiveness can make it challenging for those relatively new to Hoon to decipher programs.

Code accessibility must be a priority for userspace Hoon. We can support developer learning and entry into the ecosystem by defining a subset of Hoon syntax that we adhere to in all of the appropriate guides and some of the userspace code.

Let's call this subset "Hoon Prime" (H´). Hoon Prime will be a code discipline within Hoon to promote a clear pathway for newer developers to understand the nature of Hoon programs before needing to grapple with the entire panoply of syntax and runes. Hoon Prime may not alway be the most concise Hoon code, and it will likely fail to maintain certain well-established preferences. (Even if there is a "better" way to write it, we should prefer this restricted discipline in H´.) Hoon Prime will allow us to onboard developers into producing generators and agents from %base-distributed models as quickly as possible.

Programming Style

Runes

The first concerns are to limit the scope of runes that must be learned to the top 27 most important ones, with another 27 context-specific runes included for particular code constructions.

  • %: cencol
  • :* coltar
  • %= cenhep (sugar preferred but not mandated)
  • |= bartis
  • ^- kethep
  • %~ censig
  • =/ tisfas (specifically preferred over =+ and =-)
  • ?: wutcol
  • ?~ wutsig
  • %- cenhep
  • |- barhep
  • :~ colsig
  • :- colhep
  • ?- wuthep
  • .+ dotlus (only as sugar syntax +())
  • => tisgar
  • |% barcen
  • |. bardot
  • !! zapzap
  • ;: miccol
  • ?+ wutlus
  • |^ barket
  • ~& sigpam (with logging levels)
  • |_ barcab
  • $% buccen
  • $? bucwut
  • $: buccol

The following are allowed in specific contexts or forms:

  • ^= kettis (only as sugar syntax p=q)
  • =< tisgal (illustrative in a few particular cases)
  • =. tisdot (illustrative for "edit a variable")
  • .= dottis (only as sugar syntax =())
  • ;~ micsig (only for parsers)
  • ^: ketcol (only to explain structure v. value mode; prefer sugar syntax ,)
  • ^* kettar (only as sugar syntax *)
  • ?> wutgar (only for type resolution)
  • ?& wutpam (only as sugar syntax &())
  • %+ cenlus (as contrastive with %- and %:)
  • =^ tisket
  • |* bartar
  • =* tistar (only standard agent boilerplate)
  • ~| sigbar
  • ?! wutzap (only as sugar syntax !)
  • ?| wutbar (only as sugar syntax |())
  • !> zapgar (mainly for illustration)
  • !, zapcom (mainly for illustration)
  • != zaptis (mainly for illustration)
  • $_ buccab (only as sugar syntax _)
  • ;; micmic
  • ?^ wutket
  • ?@ wutpat
  • !< zapgal
  • ;/ micfas and other Sail runes (except in Sail materials)
  • .^ dotket
  • ;< micgal

The following should be specifically avoided in Hoon Prime:

  • =+ tislus
  • ?= wuttis
  • ^+ ketlus
  • ?. wutdot
  • ~ sig runes generally, aside from ~& sigpam
  • ?< wutgal
  • =| tisbar
  • :_ colcab (mentioned in resources but not used in H´ code)
  • =? tiswut
  • :+ collus
  • =- tishep
  • :^ colket
  • %^ cenket
  • =, tiscom
  • %_ cencab
  • ^? ketwut
  • ^. ketdot
  • |~ barsig
  • %. cendot
  • |$ barbuc
  • |@ barpat (unless needed for |* wet gates)
  • ?< watgal
  • .* dottar (except in Nock materials)
  • =; tismic
  • =: tiscol
  • ^| ketbar
  • %* centar
  • |: buccol (prefer $_ buccab in irregular form)
  • !? zapwut
  • =~ tissig
  • .? dotwut (except in Nock materials)
  • ^~ ketsig
  • ?# wuthax (decision may be revised vis-à-vis ?= wuttis)
  • |? barwut
  • !@ zappat
  • ^& ketpam

To reiterate, these are not claims about Hoon generally—Hoon Prime is a pedagogical code discipline, not the future of Hoon.

Sugar Syntax

Sugar syntax should be deployed thoughtfully.

The following syntax expressions are explicitly blessed:

  • .+ dotlus (only as sugar syntax +())
  • ^= kettis (only as sugar syntax p=q)
  • .= dottis (only as sugar syntax =())
  • ^: ketcol (only to explain structure v. value mode; prefer sugar syntax ,)
  • ^* kettar (only as sugar syntax *)
  • ?& wutpam (only as sugar syntax &())
  • ?! wutzap (only as sugar syntax !)
  • ?| wutbar (only as sugar syntax |())
  • $_ buccab (only as sugar syntax _)
  • $? bucwut (as both ?() and explicitly)

The following constructions are explicitly barred:

  • Do not use tag+value; prefer [%tag value].
    • See the note on each below.
  • Do not use `this for an empty set of cards; prefer [~ this]. (The structure is the same but the intent of a unit is different.)

Specific rules will accustom developers to use patterns before running into deeper concepts like the Hoon type system. These may be controversial but each has a specific pedagogical motivation.

  • Prefer head-tagged unions of values when multiple kinds of values should be included, e.g. in a $? bucwut type union.
  • Type unions can only be made over constants. (No ?(@t tape) sorts of expressions.)
  • Explicitly mark lists as such, rather than relying on null-terminated tuples.
  • All : col runes can be used when explaining rune alternatives.

Literate Programming

Hoon Prime should also adopt a standard of literate programming. Several example files already display such a self-documenting nature:

Our userspace code accessibility model should allow contributors from widely different skill levels to interact. We need to surface and document code patterns as cleanly as possible in the code we distribute.

We need to upgrade as much code as possible to A standards per the style guide.

Agents

Agent files should hew to the following standards:

  • /lib/agent-name.hoon
    • There should only be one /lib file per agent.
    • If JSON handling is necessary, store it in a file /lib/agent-name/json.hoon with ++dejs and ++enjs arms.
  • /mar/agent-name/action.hoon
    • There should be one /mar file per agent action.
    • Agent actions should be named from action, update, result, effect.
    • Defer all JSON handling to the appropriate mark, never in the main /app file.
  • /sur/agent-name.hoon
    • There should be only one /sur file per agent.

Prefer these constructions:

  • =* this .
  • Use the agentio library when possible.
  • Use Rudder and Schooner when possible.
  • Use (pole knot) over path in agent arms. (Just don't teach path in beginner materials in that context.)
  • After it has been introduced pedagogically, prefer the nested core pattern about 50% of the time. Show both versions (conventional and nested core) when feasible. Treat the nested core pattern as a release version refactor rather than a starting point for many learner cases.

Rules

  • Do not weigh heavier expressions down to the bottom with rune inversion (like :_ or =-). (Some of the Hoon Style Guide is strategically deprecated for Hoon Prime code.)
  • Do not privilege three-letter and four-letter names in userspace. There could still be a naming pattern (like five-letter words) that increases the code aesthetics (gap alignment in blocks &c.).
  • Be careful with metaphors. A good metaphor can illuminate, while a bad one will obfuscate. Still, have fun.
  • In working with type unions, use each to distinguish values. All values should be head-tagged semantically, and this need not match the aura form (as with +scot); that is, prefer %text instead of %t for a string.
  • Userspace apps put out by the Urbit Foundation or submitted in completion of a bounty should include tests and docs (see below).

Unit Testing and Docs

We need to include comments and unit tests so people can see how and why code works like it does.

  • /tests/agent-name/tests.hoon with multiple test files permitted if desired.

Documentation should be compatible with %docs.

  • /doc/agent-name should include the appropriate Udon (Markdown) files.

References

This document has benefited from a decade of Hoon code and organic best practices.

@bonbud-macryg
Copy link

If tag+value is barred, I'd say tag/value and value^value should also be forbidden.

@sigilante
Copy link
Author

Agreed. I didn't enumerate everything but given Hoon it's probably safer to say what's allowed than what's disallowed...

@jackfoxy
Copy link

I suggest ordering the rune lists in the doc by rune. Makes it a little easier for the reader to reason about, seeing all the | runes together, for instance.

@sigilante
Copy link
Author

I should do it both ways in a table—the current ordering is from most frequent to least frequent.

@jackfoxy
Copy link

I learned things reading this, which I expected. Some derivative of this doc should be accessible on the UF website eventually.

The link to https://developers.urbit.org/blog/rune-frequency-202212 alone was worth discovering.

What is the alternative to ?= on the avoid list? I use this rune all the time. I haven't found that type annotation can save you from needing this every time.

The problem with not teaching `this format is once students start reading hoon code from other authors they are going to see it all the time and be confused.
But then they're going to see a lot of other irregular syntax too, so maybe bookmaring the irregular syntax page should be emphasized around mid-course.

Prefer head-tagged unions of values ...

Yes, this. I would have been spared a lot of grief if I had beaten this into my head earlier.

Type unions can only be made over constants. (No ?(@t tape) sorts of expressions.)

I don't understand the motivation for this. IMHO this is legitimate type theoretical usage.

I think what you mean by /lib/agent-name.hoon is /lib/<agent name>.hoon not /lib/agent-<agent name>.hoon, correct?

Use the agentio library when possible.

Well, then I should be using it...where is it?

I don't feel meaningful hyphenated names ruin the hoon aesthetic and makes it easier to understand my own code. I realize this is a heretical dogma and expect to be arrested, tortured, and burned at the stake someday.

Documentation should be compatible with %docs.

From what I gather there's a lack of interest in %docs among the "though leaders". Likewise udon. For sure documentation should be doc cord compliant.

@tinnus-napbus
Copy link

?= is a very essential rune imo, I use it extensively, more than its ?-/?+ siblings. Especially if you're emphasising head-tagged unions and things like that

Wrt agentio, it's kinda not a great library - minimally useful and only in some cases. Most base & tlon userspace code hand-rolls helpers like scry and card constructors, you rarely see agentio usage. I'm not sure I'd put much emphasis on it.

@tinnus-napbus
Copy link

wrt to the docs app, there's a bunch of changes it needs to make it much better but it's been low priority so has been neglected. Writing udon in particular sucks. Tirrel had been doing a grant for a markdown parser and I had anticipated having that months ago but afaik it got half-done and then they shelved it cos they had other priorities. I'm gonna start working on a markdown parser as a side project and that would make it much better. I also want to build doccords browsing/rendering into the docs app. I also wanna redesign the front-end and change it to a more stateful model and use eyre caching and some other things. I think the basic concept of the app is still a good idea but it needs a bunch of work to be really useful.

@hoclun-rigsep
Copy link

Here is your resident hoon ultraconservative with an off-the-cuff reaction to
this memo.

The first concerns are to limit the scope of runes that must be learned to
the top 27 most important ones

In theory sure, when you teach your child English you start her out with a
subset of the vocabulary. In practice this makes me nervous. The entire rune
lexicon numbers around a hundred and they are organized into a single-digit
number of families. Hoon very deliberately isn't Lisp, it is a core
proposition of the language design that a vocabulary of this size is
absolutely trivial for a human brain, and I am worried that the disclaimer
that follows this section will not prove adequate. All Runes Matter.

In particular I think deprecating =+ is a big mistake—not having to name
everything is a good thing and students should be encouraged from the start to
develop a sense for when applying a face is desirable and an understanding
that it often isn't. (This point points to a larger issue regarding which I
would take the position that we should, far from being somehow embarrased by
it, embrace our rich tree-addressing syntax—judiciously of course.)

  • Do not use tag+value; prefer [%tag value].
    • See the note on each below.
  • Do not use `this for an empty set of cards; prefer [~ this]. (The
    structure is the same but the intent of a unit is different.)

Even I agree with these two, especially about the empty set of cards.

  • Do not weigh heavier expressions down to the bottom with rune inversion
    (like :_ or =-).

In a way my objection to this one reflects my view of the issues at hand.

@sigilante
Copy link
Author

Hmm, that's an interesting direction too: we have experimented with more tree-based thinking in Hoon School and at minimum a section in subject-oriented programming on =+ tislus with subject retrievel would be a compelling treatment.

@hoclun-rigsep
Copy link

Yes—along these lines, if you really want to withhold some of the sacred runes from total novices, withhold the | runes, |% excepted. Until the novice groks that a core is a [battery payload], a gate a [one-arm-battery sample context] etc, he gets no sugar. Call it Hoon Double Prime.

@bonbud-macryg
Copy link

I'd have appreciated having had core-nature drilled into my brain earlier. Though thinking back to Hooniversity it was nice to start with "use |= to define inputs and use ^- to define outputs" as a total scrub who wanted to get writing his own little functions quickly.

@eamsden
Copy link

eamsden commented Oct 6, 2023

=?, ?=, :_ and a few others are not runes you want to ban if you want readable, maintainable code.

=?  a.thing  conditional  new-value
rest

in the alternate

=.  a.thing  ?:  conditional  new-value  a.thing
rest

trivially violates DRY

?# is not even merged yet is it?

@eamsden
Copy link

eamsden commented Oct 6, 2023

(This proposal came up in a discussion thus my comments on it now)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment