Closed ajlamarc closed 1 year ago
I'll add some basic reasoning. Most developers won't be writing hoon full time. Instead, the "set and forget it" nature of Gall agents means they will be referenced rarely - nuance in syntax will be forgotten, and it's expensive to reference the documentation every time you see a :^
or equivalent. Minimizing the number of runes / conventions used will improve developer productivity.
I believe that =+
and =-
can be served by =/
in about all cases.
=?
is debatable. I think it's easy to forget, and the below syntax is more readable (set var to var if a == b, else set to a). It makes it more alike a similar expression starting with =/
.
=. var
?: =(a b)
var
a
=:
should be a series of =.
for legibility.
%_
might be useful, in practice I've only used %=
other %
runes can be served with %-
and cell samples, I believe.
:+
and :^
are extra runes, and harder to see their children, than a :*
with a terminating ==
.
Positive/Negative assertion is more complex than "if X is true then crash". It also doesn't allow for error messages that are helpful when debugging.
mar
files were the most confusing part of hoon to me, so I'm trying to keep them as static as possible and defer logic to a library file.
Remembering all of the different syntax for issuing pokes / scries / subscriptions requires constant reference to documentation. I just found out about agentio
but I believe it could reduce burden there.
premise i disagree with the founding premise.
tis
indirection is best used to make programs pretty, after they are functional.
i think it is ok to tell new hooners to avoid indirection using:
=-
=;
i disagree with all other premises, most vehemently with =+
which I think is
fundamental. if you want to multiline something with data inside, it's a must:
=+ ^= test
$: this=@t
that=@t
==
!<([@t @t] !>(['this' 'that']))
cen
i disagree the most with cenket - makes no sense to exclude here, see col
col
i feel like the runes for col are the easiest to grok, and share patterns with
other runes. it would be prudent to learn them here where they are legible.
wut
don't use assertions? at least pick one you don't want me to use.
helper/nested core i have no issue here
agentio
i personally do not like it but i see it used elegantly elsewhere.
i would prefer we call this a wash and not advocate for/against.
(pole knot)
this isn't a good newbie practice, imo.
sur
if the files are per agent, it's strange to me to include the agent name in
the structure (e.g. tome-update
). i do not think this should be recommended.
mar
i don't think this should be so prescriptive.
lib
i do however, think introducing a json parsing pattern and explainig how
marks as described above fit into it is a separate and worthy activity.
i don't know enough about programming in the wild to know if this is true:
Since Hoon is also highly irregular, this is a big roadblock to understanding other developers’ code.
as compared to other languages. is hoon irregular? it doesn't seem so to me. it seems most regular.
I wouldn't include any suggestions that prohibit the boilerplate of, e.g., rudder. Rudder is not permitted under this framework and it therefore is untenable for me. I'll call this passing the paltest. If I can't integrate paldev products in my hoon, I can't use the framework.
The basic reasoning you gave sounds like this is trying to define a set of runes/hoon style which is more minimal than the full set, but still plenty to get you off the ground and writing real-world hoon. This is a good aim, I believe hoon school takes this approach also, but probably not something "the" style guide should adopt. The style guide is for code style guidelines in the general case, not code style catered towards a specific audience.
(Alternatively, we can point to the style guide's wording, "our way of writing Hoon code", and say it's talking about kernel & core distribution code specifically. In that case, there is a specific audience: people who do write hoon full-time, or those who want to do work in the same context.)
Minimizing the number of runes / conventions used will improve developer productivity.
Doubtful to me whether this is actually true. If anything it's probably heavily context-dependent.
There's all kinds of things I could pick at here, but probably not worth it until we conclude whether we want to turn the style guide towards this direction in the first place. "Accessibility-maxed hoon style" has its place, but I don't think the "official" style guide is it. (Happy to still give more detailed thoughts in dms or elsewhere if you really want them.)
Agreed with rabsef on all points. Most of all, I don't buy the premise that reducing the size of the rune set changes the difficulty of grokking Hoon. Most of the runes you recommend using use ones you don't recommend under the hood!
And as long as we're proposing style guidelines, I think this proposal is missing the most obfuscatory practice in userspace code today: the use of kernel-style 3 and 4 letter faces everywhere. Once you learn the runes, all Hoon is equally readable. Specific runes don't change this, but what does is when you have to internalize a new metaphor or set of metaphors (often nonsensical) for every app you read.
For the record I agree with almost all suggestions by the @ajlamarc . A lot of people like to make paintings using hoon and reading code can get very annoying.
That said Urbit devs are what we are and herding cats never take us anywhere. Guess we'll have company wide style guides and that'll be that.
I'm inclined to accept @dr-frmr's suggestion that names, rather than runes, and patterns (again rather than runes), make reading harder.
In that direction, I would imagine you @polwex would be against nested core patterns that result in lines like:
ca-abet:ca-prep:ca-read:ca-rite:ca-this:ca-that:(ca-abel:calk mar vaz)
I don't think the suggestions above fully solve for your concern.
Sincerely, a painter.
A lot of people like to make paintings using hoon and reading code can get very annoying.
Runes are the only thing saving us from this! Learning every rune is a hard ceiling on complexity and from there, you get full readability (accounting for variable naming).
Never: abbreviate a label; pack characters (try to reduce the length of labels, or make lengths match up between parallel labels); use intentionally vague and meaningless words; or use nonsense text that isn’t a word.
The existing style guide already has a strong opinion against bad names.
IMO this style guide shouldn't be accepted into official docs. That would imply its contents are generally agreed upon by userspace developers, which isn't true. Plus it imposes on hoon devs that they shouldn't make full use of the language, which I'm against on GP.
I do think the broad idea here is worth considering, though. When making stylistic decisions in userspace code, there could be a lot to gain from catering to an audience that:
Beginners could read your agent with minimal context-switching to hoon documentation, and then write their own agent in the same basic style as yours. If beginners aren't led by example, they might not know that it's possible for them to write a good agent without having to first become a hoon expert. The barrier to entry for userspace dev could go a lot lower.
Runes are the only thing saving us from this!
A few runes are doing the same thing but inverting the order of the arguments they take. e.g. :-
:_
Which is fine. I understand painting is fun. But it does get annoying to read. No other language has stuff like ?:
vs ?.
. Well ruby if
kinda does.
On Earth there's two kinds of languages, right? Python-ish (one single way to do one thing) and Ruby-ish (many different ways to do the same thing). Hoon is surely Ruby-ish in this divide. 70% of runes are sugar over the real basic runes and there's a lot of runes which are really just painting-enablers.
Which again, once you get used to it is quite endearing. Like all languages, natural and computing, there's a learning curve but once you're done it's effortless. At least we don't have macros.
As for stuff like @rabsef-bicrym
ca-abet:ca-prep:ca-read:ca-rite:ca-this:ca-that:(ca-abel:calk mar vaz)
Yes I do think it's ugly. Very. I'm personally not a fan of nested cores, they feel like an OOP-ish kludge in a functional language.
But they're here to stay, and the kernel uses them heavily. So given that we could at least have a nicer syntax sugar to do nested core calls. Pipes would be nice. If Hoon is to be a painting language we might as well make sure all our paintings are pretty.
One good idea would be for Hoon School to start lessons with only the basic runes, zero sugar. I only really started grokking subject-oriented programming once I made a point of doing that myself.
I think it's worth considering that Urbit devs are currently way more competent than an average programmer, so our tolerance for "number of keywords" is a lot higher.
We write Hoon so we can use Urbit's networking + auth stack, not because of the language itself. So looking at successful languages (Python) implies that nonessential keywords (runes) should start being removed from the lexicon, and eventually the remaining representable in human-readable words. (Have floated the idea before of a CoffeeScript that compiles to Hoon.)
Understandably current "Hooners" will fight against this. While @rabsef-bicrym's code is surely art, I can barely understand it.
Keywords over hoon have already been implemented and abandoned. Can anyone surface some of the old reasoning behind that? Seems pertinent.
IMO more would be accomplished with just a VSCode extension that describes a rune onhover and provides a link to docs for that rune.
nonessential keywords (runes) should start being removed from the lexicon
Code style is very complicated. Hard and fast rules don't work in every situation. IMO the common "vernacular" for userspace hoon will naturally include fewer runes as new devs start shipping code without having known every rune, and newer devs mimic that code style. This is naturally balanced by the need to clean up long, strung out programs. Experienced devs don't need to take part in that process.
This thread by @philipcmonk is relevant
intermediate hoon programmers use =/ not at all, contorting the code to avoid it. Sometimes it's the clearest technique.
It's very difficult to create "optimally styled" hoon code making full use of the language. There's a perfectionist atmosphere in urbit which comes from the kernel work. Thats an OK attitude for the kernel, but not for userspace. It's unreasonable to expect people to read and write "optimally styled" hoon when they're creating their first urbit app. Although no one is really bullying noobs about that, it could probably be made more clear that "unoptimal" hoon is perfectly OK in most cases. There are pretty good local maxima that newer devs are free to use without forcing anything on experienced devs.
I think it's worth considering that Urbit devs are currently way more competent than an average programmer
Hard disagree, for any fair measure of our non-Urbit peers!
We write Hoon so we can use Urbit's networking + auth stack, not because of the language itself.
This is also not true for "we", though maybe it is for you -- lots of people are interested in Urbit as a purely functional programming environment. The "Nock VM" is going to be used for smart contract writing and hopefully many more things in the future, some of which may not even touch the event loop or auth stack of Urbit. If you want to create a more prescriptive language for a specific varietal of Urbit app -- the genre of social app / messaging app that doesn't need Urbit for much more than the p2p network and ID system -- that's a good idea, though I think your work with tome DB basically already handles that case. But Hoon is valuable as an existing, socially and compiler-ly agreed upon set of macros over Nock, and broad sweeping suggestions to alter the social side of this consensus seem harmful.
I believe there might be 3 projects here. I will conclude this thought with a request:
Naming Conventions - in user space it is reasonable to suggest that people use human-legible kebab case, descriptive faces for anything that has a face. I am fine with someone suggesting to new users that they, e.g., eschew "mar" for mark
and "vaz" for vase
(a common convention in my work, and yours AJ).
Highlighting "Easy" Runes - I think it is reasonable to give new Hooners a delimited, functional list of runes that will let them get started without memorizing everything. I believe @Fang- and @bacwyls (and others) spoke to that above. It strikes me that this might already be done in Hoon School as it is now taught (and has been explained above).
Suggesting a strict style for Userspace based on (1) and (2) - I take @bacwyls's contention that this will naturally occur and then dissolve as programmers start, become more familiar with the language, improve their programs and utilize additional runes to clean up style. I don't think this is a necessary activity.
Request: If we want to continue to use my code as an exemplar of "styled but illegible", I would ask only that the person making the contention submit one PR clarifying portions of my code in any of my applications, before attempting the Rabsef Gambit as I will now refer to it.
Hoon grew organically for the authors to serve the needs which naturally arose in systems programming for Urbit. This means that we can argue about rune usage in three ways:
Whether a rune does what it is intended to do in a useful way. The existence of a long tail of Urbit rune occurrence reflects the utility of many of the rare runes: they do one useful thing well that was needed at some point in time for Urbit to work correctly and expressively. ~|
sigbar should not be used indiscriminately, but it should be used in certain cases—perhaps it should only be recommended in kernel and middleware (/lib
) code instead!
Whether a rune does what it is intended to do in a correct way. Most of them do, but there are some known exceptions. ;<
micgal needed to be sanitized. ?=
wuttis should detect list
.
Whether the use of a rune unnecessarily obfuscates or increases code legibility. =+
tislus can absolutely be the correct rune in certain cases. +*
lustar is controversial but most userspace developers find it compelling within the door context as boilerplate.
Any style guide, including the current one, would be much more compelling to me if it not only expressed strong opinions—which I am fine with whether I personally hold those opinions or not—but demonstrated in production code the given discipline. (@ajlamarc does refer to tome-db
at the end.)
In fact, a long guide which discusses and illustrates WHEN each rune IS preferable and useful would be amazing. When and how to use =-
tishep, for instance? Another useful way to surface this kind of discussion would be a series of "How I Work" blog posts, wherein opinionated arguments can be made and justified, then used or not as individual developers and teams prefer. (I would welcome any contributions in this vein.) Style guides for particular dev teams also make sense, e.g. Assembly Capital or Holium or Quartus.
@ajlamarc you are probably working on documentation for TomeDB as well. I would welcome an App Workbook entry discussing how and why a version of it does what it does, and such a tutorial would be a fine place to include arguments for a particular style.
App mark and structure styling is more in flux, and while I have hewed to a discipline of action
/update
in my more recent code, I think there is a case for effect
or others for different kinds of peer actions, reserving action
for generator-driven pokes.
A style guide is a good learning tool, and a case can be made for company-level production code. Ultimately tho: Rules are boring. Embrace digital anarchism.
Before enlightenment, chop wood and carry water. After enlightenment, chop wood and carry water.
Hoon grew organically for the authors to serve the needs which naturally arose in systems programming for Urbit. This means that we can argue about rune usage in three ways:
1. Whether a rune does what it is intended to do in a useful way. The existence of [a long tail of Urbit rune occurrence](https://developers.urbit.org/blog/rune-frequency-202212) reflects the utility of many of the rare runes: they do one useful thing well that was needed at some point in time for Urbit to work correctly and expressively. `~|` sigbar should not be used indiscriminately, but it should be used in certain cases—perhaps it should only be recommended in kernel and middleware (`/lib`) code instead! 2. Whether a rune does what it is intended to do in a correct way. Most of them do, but there are some known exceptions. `;<` micgal needed to be sanitized. `?=` wuttis should detect `list`. 3. Whether the use of a rune unnecessarily obfuscates or increases code legibility. `=+` tislus can absolutely be the correct rune in certain cases. `+*` lustar is controversial but most userspace developers find it compelling within the door context as boilerplate.
Any style guide, including the current one, would be much more compelling to me if it not only expressed strong opinions—which I am fine with whether I personally hold those opinions or not—but demonstrated in production code the given discipline. (@ajlamarc does refer to
tome-db
at the end.)In fact, a long guide which discusses and illustrates WHEN each rune IS preferable and useful would be amazing. When and how to use
=-
tishep, for instance? Another useful way to surface this kind of discussion would be a series of "How I Work" blog posts, wherein opinionated arguments can be made and justified, then used or not as individual developers and teams prefer. (I would welcome any contributions in this vein.) Style guides for particular dev teams also make sense, e.g. Assembly Capital or Holium or Quartus.@ajlamarc you are probably working on documentation for TomeDB as well. I would welcome an App Workbook entry discussing how and why a version of it does what it does, and such a tutorial would be a fine place to include arguments for a particular style.
App mark and structure styling is more in flux, and while I have hewed to a discipline of
action
/update
in my more recent code, I think there is a case foreffect
or others for different kinds of peer actions, reservingaction
for generator-driven pokes.A style guide is a good learning tool, and a case can be made for company-level production code. Ultimately tho: Rules are boring. Embrace digital anarchism.
Before enlightenment, chop wood and carry water. After enlightenment, chop wood and carry water.
I've enjoyed reading all these comments. My trite response to Rules are boring. Embrace digital anarchism would perhaps be Code itself is not art. What the code builds is.
I think a series of different opinions on Hoon style would enlighten developers on what a good strategy to follow might be. Style guides for dev teams will become more important as those teams grow.
Well obviously I'm an irredeemable troll there... :)
I've been thinking about revisiting this. While I'm not willing to go as far as redesigning Hoon for the time being, I think that defining a subset of runes and limited sugar syntax that is present in the docs (outside of kernel stuff) and official userspace apps and generators would be a good thing to build for.
I'm going to post a full gist on this soon, but right now here's what I'm thinking rune-wise for Hoon Prime:
The first concerns are to limit the scope of runes that must be learned to about the top 25 most important ones, with a few context-specific runes included for particular needs.
%:
cencol:*
coltar%=
cenhep|=
bartis^-
kethep%~
censig=/
tisfas (specifically preferred over =+
and =-
)?:
wutcol?~
wutsig%+
cenlus (as contrastive with %-
and %:
)%-
cenhep|-
barhep:~
colsig:-
colhep?-
wuthep.+
dotlus (only as sugar syntax +()
)=>
tisgar|%
barcen|.
bardot!!
zapzap;:
miccol?+
wutlus|^
barket|_
barcabThe following are allowed in specific contexts or forms:
^=
kettis (only as sugar syntax p=q
)=<
tisgal (illustrative in a few particular cases).=
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 &()
)=^
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);;
micmic?^
wutket?@
wutpat!<
zapgal;/
micfas and other Sail runes (except in Sail materials).^
dotket;<
micgalThe following should be specifically avoided in Hoon Prime:
=+
tislus?=
wuttis=.
tisdot^+
ketlus?.
wutdot~
sig runes generally, aside from ~&
sigpam?<
wutgal=|
tisbar:_
colcab=?
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)!?
zapwut=~
tissig.?
dotwut (except in Nock materials)^~
ketsig?#
wuthax|?
barwut!@
zappat^&
ketpamTo reiterate, these are not claims about Hoon generally—Hoon Prime is a pedagogical code discipline, not the future of Hoon.
I think a hoon-prime guide would have:
=.
presented as: "edit a variable",
?.
: "if not",
=|
: "declare a variable without initializing",
and maybe :_
and irregular form since it's so common.
%=
and %~
are probably better presented as sugar.
;:
can be safely ignored imo.
It would be cool if docs for each rune showed how important it is to initially learn based on frequency data. Or some natural way to communicate "You (hobbyist) are over the learning curve once you know these few minimum-viable-runes." and "A ton of this documentation is for onboarding and supporting full time system devs, you can write good code much sooner than it may seem."
More broadly speaking, hobbyist onboarding would benefit from a clear path to a "meaningful" program in <1 weekend. Something that might help here is for existing urbit apps to create more scripting opportunities and documented CLI flows to accomplish little things.
;:
shows up in unit tests—they become awkward without that rune.
Thank you all for your contributions to this important discussion on conventions.
I am going to close this issue on the repo and move the discussion to the nature of a userspace code discipline, Hoon Prime.
Comment on the first version of the Hoon Prime proposal here: https://gist.github.com/sigilante/57109dafa344631f55d72bf5c7fd2fcd
Per @jalehman's request, this post should make it somewhere into the documentation (the next page after the current style guide). But, there's discussion to be had on its suggested conventions.
Runes to avoid
=+
=-
=?
=:
%_
,%:
,%^
,%*
:+
,:^
?>
,?<
Other discussion points
agentio
(pole knot)
overpath
sur
hierarchymar
hierarchylib
hierarchyted
files?Will turn into a PR once there's some level of agreement.