102 comments

[ 0.19 ms ] story [ 181 ms ] thread
Long story short, this quote:

> Lisp masters beware: Hoon [a b] is Lisp (a . b), Lisp (a b) is Hoon [a b ~]. ~ means nil, with value zero. 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.

along with the knowledge that the people writing this like really strange names (tile, gate, vase, fish) and 2 part symbols (++ += =^) is all you need to know.

> 42 and 0x2a are actually the same noun, because they're the same number. [...] But semantically, 42 has a decimal span and 0x2a hexadecimal, so they print differently.

A babbling load of twaddle which in and of itself wouldn't be a crime were the word "semantically" not involved as an abuse victim.

(comment deleted)
> Lisp and Hoon are both pair-oriented languages down below, but Lisp has a layer of sugar that makes it look list-oriented.

You know, I always wondered about where we should draw the line when describing Lisp as a language family vs. Lisp the original reference implementation, vs. Lisp the originally popular implementation (either CL or Scheme, take your pick). Because I think most people agree that Clojure is a valid Lisp, yet it's not based on pairs like Lisp traditionally is.

Really should lead with an end example then deconstruct how it works. But I get the feeling they are trying to be intentionally obtuse, either to show off cleverness or as performance art. Perhaps that's a bit cynical.
>But I get the feeling they are trying to be intentionally obtuse, either to show off cleverness or as performance art

Same here. I really don't see the point of renaming well known and defined concepts

We're not trying to be intentionally obtuse. In general, we try to only create new names when the old names are for a sufficiently different concept. Perhaps we go slightly too far in doing this, but much of the "the terms are alien" feeling comes from the concepts beings actually different than you're used to.

This is the start of a bottom-up style tutorial, but if you're interested in a top-down tutorial, may I suggest this one from one of our open-source contributors: http://hidduc-posmeg.urbit.org/home/pub/hoon-intro/

Well I mean of course you have to say that, kinda ruins the joke if you keep elbowing people and going "pretty clever of us eh?"
> In general, we try to only create new names when the old names are for a sufficiently different concept.

You renamed the godforsaken PUNCTUATION, man. What exactly is the different concept behind an equals sign in Urbit that you have to call it tis? Or the colon, what conceptually did you change about the colon that it's got to be a col or the comma that it's a com?

This is true and is an exception. Even though punctuation is punctutation, we speak it so much more than most other people, so we've found it convenient to give it convenient, one-syllable names. Using the new names is, of course, completely optional. End-users will never even see them.
How many times a day do you want to say "percent equals" or "tilde ampersand," rather than "centis" or "sigpam"? Worse, you may not spend a lot of time reading code out loud, but when you read your brain still thinks the sounds.

Of course, you're free to prefer keywords to digraphs. But sometimes I worry that there's a reason the 787 looks just like the 707 [1], and it's not at all about airplanes...

[1] http://idlewords.com/talks/web_design_first_100_years.htm

> How many times a day do you want to say "percent equals" or "tilde ampersand,"

Zero if I can help it. Even if I'm talking about code aloud, I normally do so semantically, saying what the code is doing, because people are more than capable of reading the code itself. (Same premise as commenting code.) If I'm actually reading the exact symbols aloud, a rare occurrence, then I'll say things like "mod equal" or "mod eq", "tilde and", etc. If I'm reading "#include <stdio.h>", I'll say "include standard I O", not "pound include space angle s t d i o dot h angle newline".

> Worse, you may not spend a lot of time reading code out loud, but when you read your brain still thinks the sounds.

No, it absolutely doesn't. Learning to avoid reading-aloud internally is one of the first things taught in speed-reading of prose, and the same concept applies to programming.

> Of course, you're free to prefer keywords to digraphs. But sometimes I worry that there's a reason the 787 looks just like the 707 [1], and it's not at all about airplanes...

That may be, but what you've done here is on par with APL saying "you need a completely non-standard keyboard and character set to program this language", but worse.

If you want to introduce new names for symbols, that's a separate exercise from trying to make a new programming language; the latter is quite hard enough.

But worse?

I assure you that we're not shipping Urbit with non-standard keyboards printed with "com" rather than ",". If these syllables rub you the wrong way, you're perfectly free to pronounce them any way you like.

Most people aren't speed-readers. I am, but only because I learned at an early age. I don't know anyone who speed-reads code in the sense you mean.

The grandparents post's first argument about semantic reading was much more important than the speed reading bit.
This works fine for C, which is a keyword language.

What you'll find if you look at Hoon is about 100 combinator runes, which are digraphs like "%=" ("centis") or "|-" ("barhep"). (There is of course a power-law usage distribution -- a few common runes dominate.)

If we had to invent and remember semantic names for each of these runes, that binding would really be a lot of work to remember. We could say "mutate" instead of "centis" and "loop" instead of "barhep". Remembering these bindings would be quite a lot of work.

Of course, we could use reserved words directly; you can experiment with translating Hoon into reserved words. (The earliest experiments used keywords with a sigil, like ":loop".) To my eye the keywords look hideously verbose and are easily confused with symbols.

You also lose the ability to classify, say, "|=" and "|-", as closely related runes. This is especially useful with unusual, rarely used runes; what is "|/"? You might not know, but you know that it starts with "|", so it makes a core.

> If we had to invent and remember semantic names for each of these runes, that binding would really be a lot of work to remember.

So is remembering what all the runes do; if the names and the functions relate, they're both easier to remember.

The runes (a) help you remember the combinators, and (b) are two characters long.

To get to reasonably precise, meaningful keyword bindings for this set of combinators in particular, perhaps you'd be replacing ":-", ":_", ":+", ":^", ":*", and ":~" with ":pair", ":reverse-pair", ":triple", ":quadruple", ":tuple", and ":list" respectively. This is a pretty benign and easy case, and you may have to trust me on what kind of hash this search-replace alone would turn a Hoon file into. Then again, I suppose it all looks like hash to you.

Language design is hard; you can't please everyone. Probably the best-case scenario is that you please some people, and the rest are dragged kicking and screaming. And that's a best case.

No, I actually think that having combinators like that makes sense; it's a perfectly reasonable (and very APLish) design choice to Huffman-code those kinds of operations as combinators. I've worked with Haskell code that makes extensive use of symbolic combinators, and once you get used to a given set, code using them can become much more readable. And using symbol pairs where related operations have related symbols makes even more sense.

I just don't think it makes sense to invent fanciful names for them based entirely on their symbology, rather than their function.

It's your language; you can call things whatever you want. But I've seen many languages that I would like to succeed fail or grow very slowly because they failed to engage well with prospective users. And when you already have a nearly-asymptotic learning curve, why add more to it?

We're at a pretty shallow level of disagreement here, because you'd just add descriptive names to the combinator definitions. I'm not sure this would improve the learning curve much if at all, but it couldn't hurt much either.

As I always say, there's a difference between real and apparent learning curves. I don't think your perception of the apparent learning curve is wrong. But some things are easier than they look; one of those things is remembering associations. I've seen a good number of people walk the real learning curve for this language, and it's not that steep. Hopefully reality also gets a vote.

I appreciate this clarifying comment. This certainly feels a lot like the same kind of things APL and friends did/do.

I'm almost convinced it's a bad idea, but not entirely. It gives me the feeling of conflating conciseness and simplicity - taking something that is (overly) complex and attempting to make it simple by making the representation more compact.

On the other hand, different (from the norm) can be a good thing in itself. Still haven't gotten around to play much with it. I strongly suspect that, if we consider this the first incarnation of what will be the future of this system, the third or fourth re-iteration might be getting to the core. (If you can't explain something simply, you don't yet understand it etc).

We make up new line-noise (=combinator runes) all the time in Haskell. The lens library alone has over 100, see https://www.fpcomplete.com/school/to-infinity-and-beyond/pic...

Haskell people invent and a lot of new and strange things. Renaming punctuation is not one of them.

I guess it doesn't matter as long as you never use your new names in writing. (And why would you, you can use use the symbols themselves.) But then, of course, why bother with these names at all apart from creating an oral tradition.

Speaking from experience, I guarantee you that anyone who starts saying "centis" instead of "percent equals," whether in the context of Hoon or Haskell lenses or anything, will never go back. I have to force myself to "code-switch" when speaking to the uninitiated, even in a technical context that has nothing to do with Urbit. And I'm not the only one.

I can't fault Haskellers for their daring in pushing the limits of programming languages as a UI. For instance, the idea of custom, library-specific "line noise" terrifies me. Hoon has no macros, no custom syntax, no operator overloading, etc. Once you've learned the line noise you've learned it.

The lens library in particular is a DSL by anyone's definition. Arguably (well, I'd argue it anyway), the ease of constructing DSLs is a trap that's impeded adoption of both major families of FP, Lisp and Haskell/ML. Plenty of people have made the "DSL == write-only" argument, so hopefully it's sufficient to just reference it.

Lens is in a sense the quintessential expression of Haskell; I don't know how anyone grounded in reality could expect the average Java programmer to learn and master it. And yet, everything it's doing is beautiful and true and right. Basically, resolving this contradiction strikes me as the essential problem of functional programming, if not just programming, in the next decade. I personally am very far from being the most qualified person to solve this problem. But I feel not enough people are working on it.

Interesting enough, `lens' is a bit of an outlier in Haskell land---exactly because it is too Java-like in its intricate hierarchies for some people's taste.

I like the Haskell approach to operators as just a funky syntax for functions. No need to bake any operators as keywords into your language. +, =, etc are all just part of the standard library. (I also like the Lisp stance of mostly just not caring about line-noise symbols vs letters.)

> I have to force myself to "code-switch" when speaking to the uninitiated, even in a technical context that has nothing to do with Urbit.

That's one of many reasons I wouldn't want to switch even if it was more efficient, for much the same reason that I have no plans to learn Esperanto or switch to Colemak. That's also precisely why I suggested it was much like requiring a custom keyboard.

I can say "x mod equal y" to almost anyone technical, and they'll immediately understand it as "x %= y". One syllable for understandability. And considering how rarely I actually speak exact syntax aloud, that seems well worth it. (It occurs to me that someone designing and using a new programming language seems far more likely than average to have to communicate syntax verbally.)

> Worse, you may not spend a lot of time reading code out loud, but when you read your brain still thinks the sounds.

Probably correct. But then for names you are not familiar with, the name gets mapped into the equivalent and known concept in your working memory. I usually model this as a very small LRU stack where old objects get pushed when you exceed the size. The stack is considered to be around 7 concepts deep. So just by overloading these 2 words, you're potentially reducing working memory by more than 20% (increasing mental load by much more due to constant swapping in and out of working memory). The brain is amazing

I know wikipedia is not always right(tm) but https://en.wikipedia.org/wiki/Working_memory#Capacity

"Your brain still thinks the sounds" is hilarious.
I found this tutorial somewhat more cogent than most of the previous literature... I don't mean to be a dick but did Curtis write this one or someone else?
Having watched the mental carnage caused by git's re-use of names that older VCSen used for different things afflict multiple people (myself included) I do rather see your point.

On the other hand, I really, really think that a set of englishified versions would be helpful as a newbie tool - in the same way that I often encourage early stage perl hackers to 'use English;' until the shorter names wedge themselves into their brain.

Would doing that be as simple as something like

    += if ?:
and etc. and etc. ? I think I can appreciate that once you're used to the runic versions everything is fine but, well, when I talked about urbit having a vi-like cliff-ish learning curve curtis replied talking about putting up ropes, and this seems like a pretty reasonable rope.
We definitely don't have Linus's streak of user-interface malice. (Git-related mental carnage has taken its toll on many urbit developers as well, alas.)

What you suggest reminds me of Lisp's infamous "M-expressions", but worse, because the lack of reserved words in Hoon is the most immediate tangible benefit of our syntax. We're homoiconic, and syntax looks unambiguously like syntax (and is actually extremely regular).

It's difficult to make the assertion that there really isn't much of a learning curve stick in a HN comment, unfortunately - it's based on experience learning the system myself, and helping others learn it on talk. It hasn't been anything syntax-related that trips up people actually learning the language, certainly - it's just something superficial that's easy to comment on on websites, as we've all seen around Lisp or Perl or even Python (whitespace-indented blocks).

To answer you directly, runes are the tag in the tagged union that is the type of our AST, not any kind of identifier. (Perl would probably let you assign pieces of syntax to identifiers, of course...) For example, if you have the following Hoon:

  |=  a=@
  ?~  a
    ~|  %decrement-underflow  !!
  =|  b=@
  |-  ^-  @
  ?:  =(+(b) a)
    b
  $(b +(b))
the associated AST (a noun of type ++twig) is

  [ %brts
    p=[%bark p=%a q=[%axil p=[%atom p=~.]]]
      q
    [ %wtsz
      p=[%.y p=~ q=~[%a]]
        q
      [ %sgbr
          p
        [ %dtzz
          p=%tas
          q=2.663.495.029.034.430.894.880.199.458.246.708.989.383.632.228
        ]
        q=[%zpzp ~]
      ]
        r
      [ %tsbr
        p=[%bark p=%b q=[%axil p=[%atom p=~.]]]
          q
        [ %brhp
            p
          [ %kthp
            p=[%axil p=[%atom p=~.]]
              q
            [ %wtcl
              p=[%dtts p=[%dtls p=[%cnzz p=~[%b]]] q=[%cnzz p=~[%a]]]
              q=[%cnzz p=~[%b]]
              r=[%cnts p=~[%$] q=~[[p=~[%b] q=[%dtls p=[%cnzz p=~[%b]]]]]]
            ]
          ]
        ]
      ]
    ]
  ]
(To get this, I just ran ++ream, our compiler's frontend, on that code sample.) It would be as simple as dropping in an alternate parser there that looked for, e.g., 'if' rather than '?:', which isn't actually hard because our parser combinators are really easy to use. (The actual Hoon parser is very old and due for a cleanup, unfortunately, but conceptually is not very complicated.)

I wouldn't endorse it myself, but perhaps in the future of the project someone will find it a useful thing to do and do it. It would definitely clash with the language's design sensibilities in a way that "use English" doesn't clash with Perl's - Hoon is somewhat more picky (though, what language isn't?).

Edit: I completely forgot that I was going to include a hypothetical version of the above with keywords. Here:

  defgate  a=@
  nil?  a
    error  %decrement-underflow
  dim  b=@
  runtrap  cast-to  @
  if  inc(b) == a
    b
  replace b with inc(b) in this
(The keywords are silly, but please imagine better ones.)
Actually, hoon syntax reminds me of a coffescriptish set of M-expressions for a language designed by APL developers who were told to use digraphs to get around the users' use of teletypes (edited after juped replied to add: I think juped did, but everybody else, please do read that as if said with an indulgent smile rather than a sneer, I -am- a perl hacker after all).

If anything, I could probably do fine with an S-expr-ish form -

    [ gate [a=@] [
      [nil? a
        [ error %decrement-underflow ]
        [ let [b=@] [
          [runtrap [cast-to @]]
          [if [= [+ b 1] a ]
            b
            [ recur b=[+ b 1] ]
          ]
        ] ]
    ] ]
but what's perhaps more interesting to you is the fact that I'd managed to remember 'wutcol' for ?:, which then meant that when I saw %wtcl in the output I immediately identified that branch and my brain started reverse engineering it without any real 'wtf' time, leading me to wonder if forcing people into the pronounced names earlier somehow might also help.

A more actually M-expression style might be

    gate {{a @}} {
      nil? a {
        error %decrement-underflow
      } {
        set b @
        runtrap [cast-to @]
        if {inc(b) == a} {
          b
        } {
          recur a=inc(b)
        }
      }
    }
which would probably be quite trivial to implement in Tcl, though I think losing the tall/wide principle would be an impediment to learning in a way that named runes I suspect wouldn't be.

I'm afraid you're in error about 'use English' though - to many of us it absolutely does clash with the sensibilities of the language and frankly I find it intensely grating (please do reconsider my words in that light to see if they change meaning to you, if you have a moment to do so). I would fully expect somebody who knows hoon reasonable well to find this just as grating, in the same way I'm sure a trick cyclist would find riding a bike with training wheels grating. But that doesn't mean training wheels can't be useful, even so.

Yeah - we are homoiconic (twigs are nouns), but we aren't actually Lisp.

I think with training wheels it probably pays off to be excessively empirical. See the situation in Haskell where the only reason "map" only maps over lists and not generic Functors is that people were afraid to put the word "Functor" on a newbie's screen - but many newbies are in fact confused by "map" and "fmap" both existing! So holding one's nose and accepting something inelegant for that reason should probably be done if and only if it works. So far, we haven't actually had any issue with digraph runes in particular, and I think there's a number of reasons why.

I think in practice a lot of what we actually do is aggressively subset the language - which is okay, because a great deal of the runes are in fact compiler-level macros. While we can't put this in any documentation because it would hold us to too much, the character choices in runes are actually designed to have patterns that are easy to remember in them. For instance, %-, %+, and %^ are 1, 2, and 3-ary "function application", respectively, and %. is %- with the order of its children reversed. Analogously, :-, :+, and :^ build 2, 3, and 4-tuples, respectively, and :_ is :- with the order of its children reversed (a minor irregularity because :. is somewhat awkward, especially when your comments start with ::). Finally, ?. is "unless" - it's ?: with the "then" and "else" branches reversed. So a new Hoon programmer might learn |=, along with the fact that | runes make cores, and ?:, along with the fact that ? runes are conditionals, and already be able to sorta-read the decrement gate given above.

Another sort of training wheel we have is that some of our concepts can masquerade as more familiar ones. For example, many people, including you, read the irregular form for %=, or "evaluate with changes" (seen above as "$(b +(b))") as "recursion", because most of the time you see it explicitly in simpler code it is actually being used to recurse. The reason this turns out not to be a huge deal is because there's basically no non-pathological case where %=ing $ (which is actually not syntax, but an empty name, in the code above referring to the |- trap that it's inside) is not recursion, making it an intermediate-level topic (and it's a higher-level topic that, at the lower level, %= is basically the most fundamental operation we do).

It's not surprising that you could remember %wtcl easily - a lot of people associate verbally. I do, and Curtis has made it clear that he named the punctuation because he does. The weird names make it less of a subconscious thing than it is with, say, "char *argv[]" or "s->len", and also mean that Hoon programmers in the same office can talk quickly and unambiguously (it does feel silly at first, I admit).

The biggest insight of the syntax, though, (in my opinion, which is not necessarily shared), is that fixed-arity syntactic forms let us avoid both code that tries to escape off the right edge of the screen and unsightly piles of closing brackets, because most syntax trees are greatly unbalanced. Something really prosaic that feeds into this is just the fact that all the runes are two characters wide, giving Hoon that surprisingly easy to scan "imperative" feel. You might be able to find four-character keywords (without cheating and using wtcl etc, naturally!) and make this work, of course.

Ok, so, the digraph runes are the sticking point, for me, trying to read hoon code, so far (caveats very definitely necessary). I love the tall form indentation system, and I actually do know what $() is from reading the hidduc-posmeg stuff ('recur' is common sugar for a lisp continuation-based recurse-into-somewhere-often-the-current-function - please don't confuse that with me thinking it's plain recursion) - but, well, other than ?: which fits the hook operator thing (and ? and ?? which seem pretty clear), I've not got a good way to hang memories off the rest. Some sort of set of mnemonics, even very silly mnemonics, ala 'perldoc perlvar', might make things a lot easier for me.

Can you invent a just so story that explains why % is for apply, and : is for making tuples? It doesn't have to be particularly real, I could just do with something to hang the liveware memory structures off.

(note that I am -trying- to get my head around this stuff, but I've not quite got far enough in terms of reading code for creating an installation to seem worth the effort ... but I want to, so I'm trying to find constructive ideas that'll help other people trying to learn and incidentally also me as well ;)

It's easiest for ? (conditionals) and ! (black magic), but it's not ever really completely arbitrary. (Some of this may just be stuff I made up rather than the actual etymology, but that's what you asked for and it's useful anyway - cf. traditional Chinese character "etymologies", which drive linguists mad but are great memory aids.)

$, buc - construction of tiles (which I think we have since renamed to molds). This is type-system stuff, and I think the $ is supposed to evoke shell/Perl/&c. variable sigils, which are also type-system stuff.

|, bar - construction of cores. I really think this one is just there because of the pseudo-box-drawingness of how a generic multi-armed core looks:

  |_  [a=@ b=*]
  ++  arm  42
  ++  gate-arm
    |=  a=@
    (add a arm)
  --
something with a | at the root, and then ++ arms hanging off it like... arms. Cores really read more as layout than text to me.

:, col - builds tuples. This is a punctuation mark that joins things, standing for some kind of consing operation. It's also seen in the second position in runes like $:, where it also tends to mean a consing operation.

%, cen - evaluation. Everything here is a syntactic sugar on the most general operation, %=, which evaluates something from the subject with specified changes. I find this easy to remember because it's the most important one. It's also on Tlon's logo, for what it's worth.

., dot - Direct Nock. The vast majority of . runes in use are .=, test for equality, in its irregular form =(a b). Nock is the low-level target machine, and . is a pretty low punctuation mark (_ is probably lower, but never appears in the first position in a digraph rune).

#, hax - Pretty printing. I don't think these are ever really seen in their regular form (just in string interpolation syntax, really), but pretty printing involves many hacks, and # is pronounced 'hax'.

^, ket - Type operations, chiefly casts. If you imagine types like superscripts on values, then the ^ character makes sense.

~, sig - Runtime hints, either debugging printfs or actual hints to the runtime. Imagine writing notes on some text, preceded with a squiggly line.

;, sem - Composers. Pretty much the only one you'll see in its regular form is ;~, for monadic composition, though ;: which promotes a binary gate to more arguments (say, for adding 3 numbers) might show up in tall form as well. The semicolon is a punctuation mark which composes related but somewhat standalone clauses together.

=, tis - modification of the subject. Hoon is a language where the environment is first-class - other languages have variables and scoping and the like, Hoon just has the subject, with everything from your function's arguments to the Hoon compiler and kernel in it. = is a common assignment operator in programming languages, which is a special case of modifying the subject.

?, wut - conditionals. Pretty easy, fortunately!

!, zap - black magic. Black magic deserves the ! - special honors go to !!, or "crash here". !: essentially turns on debugging mode for anything inside it - you'll often find it at the top of code files, given that Urbit is under construction.

Thanks for the feedback - this sort of thing is the main value that we get out of posting things on the net. It's much appreciated.

I like most of those but here's some alternative stuff I made up that I think may work better for me (emphasis on may and me) -

~ -> it's a hint, so it's saying "hey, this is 'ish' that" or so (~ always kinda means 'ish' in my head). or maybe we use the fact that ~ is bitwise complement and say "it's info that complements the thing"

# -> "format it for a comment"

: -> ':' connects chunks of sentence together

; -> semi-:, so does half that and half something else as well, oh and also sem for 'sem'antic composition whereas col just 'col'lects stuff together ... actually that sem/col version really makes me happy.

Glad you're finding this at least vaguely constructive, I'm rather enjoying it too.

Someone also pointed out that people have called monads "programmable semicolons".
I really like the idea of a binary tree memory model, especially an acyclic one. It appeals to the mind in the same way Lisp s-exprs do, and possibly even more: everything is either [head tail] or a number! How stupid!

As far as I know, a lot of this stuff is fairly novel as well. Imagine you could simply mmap() bytes into your process from the network, verify that it nests inside your type definition (including recursive members!), and not have to worry about following evil pointers while still allowing arbitrary-sized members. This actually happens in urbit: all network messages to apps, including those sent from a web browser to your personal planet, are just ++jam nouns with something like an executable mime type plus autogenerated type verification function

Of course, I'm pretty biased about Urbit, so your enthusiasm may vary.

It sounds a lot like google protocol buffers, or one of the many java serialization frameworks. This "jam" thing may be better integrated into the urbit architecture, but without any portability/interop with other systems/languages.
See the comment above; it's nice not to need a separate framework. At least, for Urbit to Urbit networking.

Of course there's nothing stopping you from encoding or decoding anyone else's protocols in Urbit. Or vice versa. Actually, using bit functions on bigints is a fairly pleasant way of handling binary protocols.

There's also no particular problem in generating or decoding jammed nouns on non-Urbit systems. It's a binary encoding, but the decoder is a page of code. Perhaps jam:ASN.1 as JSON:SGML. :-)

That's just about how it works with Cap'n Proto, https://capnproto.org/

No idea about if you could do it with mmap from the network, that'd be an OS thing and I suspect not possible to do from a streaming protocol like TCP in most places.

CP is one of many IDLs [1]. IDLs are great, but there is always an impedance mismatch in translating a language's native data structures to a language-independent IDL. Someone has to do this work, though there are sometimes various scary ways to do it automatically.

In any case, the idea that validating or describing external or untrusted data is the job of an additional system outside the programming language, rather than the job of the language's type system, seems mainly historical. Sending data over the network was a new and unusual use case when most of today's languages were being designed. It isn't now.

[1] https://en.wikipedia.org/wiki/Interface_description_language

Yup, I've heard of Cap'n Proto before, but unfortunately never worked with it. I was under the impression that you still had to write the specfile yourself, and that deserializing malicious data was unsafe (along with being platform specific), but that doesn't seem to be the case.

Urbit RPC is how I imagine RPC should always be: sending a message to `:talk` isn't actually a procedure call, but instead just sending a noun that it pattern matches on. You just say "send %talk on ~doznec this noun [%msg 'hello world']", with ~doznec on the other side automagically verifying the type with a living spec of code and molds and calling the correct function that takes the type you sent.

The fact that this all happens completely transparently, even for sending messages to local apps (and hence embrassing remote servers), and as a core feature of the language is great.

> deserializing malicious data was unsafe

Deserializing malicious Cap'n Proto messages is safe (barring some now security bug found in the implementation). Cap'n Proto RPC is explicitly designed to be used between mutually non-trusting parties.

From OP:

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.

It'd be useful to hear what's unclear about this.

Kinda giving away the game there.
All of that is unclear: The two distinct concepts are only (very opaquely) introduced in the next paragraph. The reference to the mathy concept of type in other FP languages is vague (basically incomprehensible to me). Consequently the final sentence carries very little information; the reference ("this") is also ambiguous.

I'd drop the quoted text and maybe the entire section ("A type system for nouns"), alternatively, move it down a few sections. I understood the examples for the most part.

I still have no idea at all what a mold is.

Happy to take PRs if you feel you can write it better. One of these paragraphs has to come before the other.

"Mathy" probably should have a link to https://en.wikipedia.org/wiki/Type_theory. My experience is that if you say "type" in the context of functional programming, people expect Bertrand Russell and have a right to expect Bertrand Russell. We're not delivering Bertrand Russell, so it would seem like false advertising.

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.

I stared at this for a moment but was unable to make it any clearer. Suppose your span is the set of all cells. A mold for this span is a function that maps any cell to itself, and any atom to a trivial cell like [0 0]. Does that help?

The definition of a mold is clear (to anyone who has had higher education in mathematics, which seems a prerequisite for this introduction). What I don't have is any intuition about what a mold is.

The example helps a little. The tutorial seems to have another example of a mold in the final section, but I'm only guessing so because of that section's title. I'll be damned if I can tell where the mold is in that section and I cannot relate it at all to the example in your comment.

From OP, in the section "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 a Hoon 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.

I do think this is clear enough. I'm not sure it's intuitive enough. The following chapters should help with that; sorry.

But is this a representation of a mold or span? It seems to describe a set rather than define a function, so the latter seems more likely, but the section header implies otherwise.
That's pretty much what a mold is. The syntax is written to look as much as possible like the description of a set, but the semantics are that you're actually defining a function.
> 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.

I think using the magic word "projection" would make this clearer. Basically, a span is a set of nouns concretely defined by a projection operator ("mold") on the set of all nouns, right?

My own math background is limited enough that this magic word doesn't naturally occur to me, though I do know what it means.

That's usually a red flag for me; I want to keep the level of modern math required really, really low. In fact, when I use math-ese in the doc, the goal is to avoid confusing people who do actually know math. As this discussion illustrates, that's quite a bit harder than one might expect.

Wait, you want to use concept that occurs in modern math, but to keep things simple, you invent new names for these concepts?

I see nothing wrong with crossing symbolic landscapes that have been trodden before, finding new (or rediscovering old) uses, techniques etc. But I think researching existing taxonomy/nomeclature can be very useful. Eg: ref other comments here: a tree is a graph that doesn't have cycles. If you say something is a tree, then that is pretty clear. Sure you can go on to define what a tree is, for those that have no background in basic graph theory -- but stacking somewhat redundant, special-purpose terms strikes me as being mostly confusing.

(Again, wrt. the symbols vs terms/concise vs verbose I'm not entirely sure I think that whole concept is a wise direction, but I'll happily defer judgement on that a decade or so)

[ed: After reading the actual article, not just the comments :-) -- it's not as bad as the comments indicate, by any means. I think the clarification on trees/cells really helped. That said, I think it would benefit from a couple of complete rewrites. It still feels like a rough draft (which is ok. Sharing is caring, share early for feedback, posting now is better than editing forever in secret etc). For me the strongest points are probably the direct contrasts with lisp, the part about trees etc.

Basically, trust in the ideas: they are not revolutionary, they are small changes on old ideas. That's fine. The sum of the system can still be revolutionary. But I think the "soft sell" would end up in a much stronger message here. And more people would grasp at least some parts of what you're trying to say/do ;-) ]

A directed acyclic graph is a graph without cycles.
An acyclic graph is a graph without cycles, yes.
I want to stick to ordinary American high-school math as I remember it. I learned "domain" and "range" in high school. I don't know anyone who defines these terms as something different. When I hear "projection" I actually don't think of set theory, I think of linear algebra, affine transformations, etc. If it would confuse me, I can hardly confuse others.

The distinction between a tree and a DAG is always a matter of interpretation. From the programmer's perspective, a noun is a tree. From the machine's perspective it's a DAG, because we share pointers and use reference counts. Either perspective can be misleading if you're expecting to think in terms of the other. If I saw the three-sentence definition of a noun, I'd assume it was acyclic, but others as we've seen above would assume it was cyclic. This stuff is hard.

The probable future of this text is that we release all the chapters pretty much as they're written, then edit it and turn it into a book.

First, apologies if my earlier comments came off as negative and harsh (along with others in this thread). I think what you're working on with Urbit looks very exciting!

> I want to stick to ordinary American high-school math as I remember it.

I can see how that would make sense, but it strikes me, personally, as an odd choice. IMNHO there are two levels of "useful" math - primary school and college. Most of what I associate with (senior) high school math ends up in the middle, mudding the waters. FWIW I agree with you on the concrete example of "projection" -- I'm not sure I agree that it's not a good term to use after some more reflection - but it's not something that I would readily reach for.

In the more general sense, it appears to me that what you're covering is basic CS, some stuff related to data types - and some basic graph theory and discrete mathematics. It strikes me as odd to invent parallel terms for things that aren't all that different, rather than just try to explain in terms of well-established nomenclature (introducing that as needed). Nothing wrong with building from basic "traditional" high-school math, but at the same time one shouldn't go to great lengths to avoid basic graph concepts.

> If I saw the three-sentence definition of a noun, I'd assume it was acyclic, but others as we've seen above would assume it was cyclic. This stuff is hard.

I suppose I'm not entirely convinced this noun concept is all that aptly named. Sure, I can see having two different texts, one that invokes ordered pairs, structs and pointers etc - and one that tries to avoid such details -- it just feels that you sometimes go overboard in re-naming things, rather than naming them.

I do agree a balance needs to be found - Haskell texts for example - tends to build from a solid cloud, on which a ladder is erected onto a branch of a tree standing on the back of turtles, and then said ladder is pulled up as the enlightened author concludes the simple tour of simple concepts.

I do think the sidebar on ordered pairs, lists and lisp is very apt. Obviously that'd make absolutely no sense to the average high-schooler?

> The probable future of this text is that we release all the chapters pretty much as they're written, then edit it and turn it into a book.

This sounds like an excellent plan. As mentioned, releasing early is good.

I'm afraid I've come off as more negative than intended again -- I do think that there are new concepts, or mixes of concepts within urbit -- but for now it is kind of hard to see what is "new", what is "tweaked" and what is merely "renamed" (or re-invented, or independently re-discovered).

Why don't you call it a projection?
If you're willing to introduce the term "projection" then I think it's illustrative and has a good connection to other metaphorically identical connected projection concepts.

"A mold is a projection function ranging over a span. As a projection function it converts any noun to the 'nearest' point in the span and is idempotent."

(Edit: just realized that everyone under the sun whose had a dose of algebra is suggesting this term. I mean to go further when descrbing it as "taking nouns to the nearest thing in the span". A mathematician or sufficiently picky reader will ask what "nearest" means—and this may be exactly the right question—but the intuitive image may be more clear.)

>Same here. I really don't see the point of renaming well known and defined concepts

I kind of like this whole renaming things thing that a few projects have been playing with lately. I think of elixir/phoenix with all their "hex" "mix" ".ex" files and other disturbing things, Rust with "crates", "cargo"... Words carry too much weight, sometimes changing a few words can force you to make the effort to learn again and you may learn something new

What else would you call cargo? It's a tool that only works for Rust. So any name is gonna be pretty much just a name, unless you take the MS approach and call it "Rust Package Manager" (RPM... oh wait.)

I do think it's fine to pick up new names when things vary significantly. In my own projects, I will often eschew names that are too generic if it'll help clarity. Like CustomerUpdateAgent. If the program mainly revolves around that, and there's even a slight bit of ambiguity over what that might mean, then give it a name that conveys "you need to know what this is, don't assume based on the name". So, perhaps in that case, "Freshmaker" is a good name for CustomerUpdateAgent.

To quote the great Scott Alexander: "because Neoreactionaries react to comprehensibility the same way as vampires to sunlight."

Joke aside, I do like what I'm reading so far about Hoon. I sense a mix of APL and LISP, but with more typing.

Yes, to me this is some kind of logorrhoea (or "diarrhea of the mouth"). I think even TempleOS sounds more clearly than Urbit...
The "navigate to top" links in the upper left kept appearing and disappearing as I moved my mouse and scrolled around, and it's extraordinarily distracting to me.
Ditto. I even moved my mouse cursor on the right side of the window, hoping it would stop.

It didn't.

I just remembered that you can right-click it in Chrome and delete it from the DOM.
Keep forgetting that, thanks for the reminder.
Yeah, you're right. Was something that sounded good, but is actually just distracting. I'll take it out.
(comment deleted)
Tududum, anyone's got it cached (Google has some JS only)?

%exit

/~zod/home/~2015.10.17..20.34.57..3a55/arvo/eyre/:<[1.104 9].[1.106 38]>

[%bad-beam %home]

/~zod/home/~2015.10.17..20.34.57..3a55/arvo/eyre/:<[1.103 9].[1.106 38]>

/~zod/home/~2015.10.17..20.34.57..3a55/arvo/eyre/:<[1.102 9].[1.106 38]>

/~zod/home/~2015.10.17..20.34.57..3a55/arvo/eyre/:<[1.101 9].[1.106 38]>

/~zod/home/~2015.10.17..20.34.57..3a55/arvo/eyre/:<[1.100 9].[1.106 38]>

Sorry for the interruption. Everything is being served from an Urbit. We have done okay under HN load in the past but may have just screwed up a hotfix.

For the time being, here's a paste of the markdown: http://pastebin.com/G3zMqZN8

I'm finding this tutorial really hard to follow. It doesn't track my mental state very well; each successive sentence that qualifies an idea doesn't follow from the last one to me.

I'm struggling to describe this phenomenon exactly, but I want to try because I'm interested in quantifying it. So here's an attempt:

When I'm reading a tutorial to a new idea, a qualification needs to follow naturally. If you tell me "this is an A", and then follow it with "note: it's not a B", that qualification only works if I would have thought, in my head "oh, is it also a B?" after the first sentence. Otherwise, I'm left in a tough spot: I believe you that it's an A, but I don't trust that I know what an A is, because I'm apparently supposed to have realized that that suggested it could have been a B as well - because you just jumped in and pointed out that it wasn't!

So such a qualification is not just a clarification; it's also narrowing down the possible meanings of A. But I didn't know about all those meanings, because all I have is the word 'A' right now, and I don't understand the complexity of that yet.

Here's a specific example:

"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)."

As of sentence #3, it looks like a noun can contain cycles. I mean, it can be a cell, and a cell can contain nouns, so that seems like a cycle. Then, in sentence #5, I'm told they don't have cycles. What? Now I don't know what to believe. I thought I understood nouns, but I don't anymore. Then, in the parens, even more concepts are thrown in. Now we're talking implementations - but I don't know what we're implementing. And we're taking advantage of acylic graph structures. Taking advantage? To do what? And the word 'although' shows up. They 'don't' have cycles, 'although' we should take advantage of their not having cycles? Either this is mis-worded or there's even more context I'm missing.

.. anyway, that got wordy. Point is, almost every sentence in this article confuses me further, instead of confusing me less. There are no 'ah-ha' moments. It's just "huh?" after "huh?".

Thanks for the detailed feedback!

As of sentence #3, it looks like a noun can contain cycles. I mean, it can be a cell, and a cell can contain nouns, so that seems like a cycle. Then, in sentence #5, I'm told they don't have cycles. What?

If we used the word "tree" and "leaf" here instead of "noun," would that help? "A tree is a leaf or a cell. A leaf is any unsigned integer. A cell is an ordered pair of trees."

It doesn't strike me that anyone reading this definition, even not knowing what a "tree" and a "leaf" are, would assume that a tree can have cycles. And then, two sentences later, if we say that a tree doesn't have cycles, I'd hope that that would settle the matter.

It's probably a mistake to bring implementation issues (like the fact that you can share a pointer rather than copying a subtree, thus creating a DAG [1]) into a discussion of this level, though. All kinds of people have different kinds of CS backgrounds...

[1] https://en.wikipedia.org/wiki/Directed_acyclic_graph

As soon as the nginx cache times out, the offending text will read:

A noun is an atom or a cell. An atom is any unsigned integer. A cell is an ordered pair of nouns.

Nouns are trees; they have no cycles. Noun comparison is always by value (the programmer can't test pointer equality). Nouns are strict; there is no such thing as an infinite noun. And nouns are immutable. There's just no way to have any real fun with nouns.

I hope this is a little less confusing. Thanks again for your feedback!

So, on this noun/cell/thing..

As I'm reading it,

    noun := atom | cell
    atom := integer
    cell := (noun, noun)
So when you say "nouns don't have cycles", my problem is that I don't know what you mean by "don't". Do you mean they're not able to have cycles? (am I unable to write A = (A, A)?) or do you mean that you prohibit it? (that you check validate nouns to avoid cycles?) or do you mean that model prohibits cycles? (it appears to allow for A = (A,A), so I don't see that).

It helps to tell me it's a tree. It's okay to use the word 'noun' if that's the word for it, but telling me "nouns make a tree, where the leaves are called atoms and the non-leaves are called cells" - that's far more useful than describing properties of trees without using the word tree.

When you mention the implementation details in "although a noun implementation should take advantage of acyclic graph structure", do you mean to say "therefore" instead of "although"? Because if nouns don't have cycles it makes sense that you can take advantage of that. The word "although", however, signals an exception, and the "although.." clause should say something that is somehow in opposition to the first part of the sentence. The nod to implementation details is probably unnecessary,but it would be harmless if not for that confusing word "although" that makes me think I've misunderstood something.

.. though, I'm really sleep-deprived today, so perhaps I am struggling to understand things that I would otherwise be able to parse. I dunno.

"Nouns don't have cycles" in two senses.

Abstractly, a noun as defined is a tree and trees don't have cycles. So there! :-)

Concretely, the Nock interpreter, which Hoon compiles itself to, has no way to construct a "noun with cycles," like your A == [A A]. I sense that this is what's really bugging you. :-)

Of course, Nock is written in C and uses a C data structure. When we manipulate this data structure from C, we can certainly construct all the degenerate "nouns" we like. This generally results in a quick trip to the noun hospital.

I'm genuinely surprised at how few acyclic programming languages exist. True, there are plenty of algorithms with faster asymptotic runtime given cyclic/mutable data structures. There are lots of ways to manage this problem, though.

And both for persistence and network transmission, acyclic structures are just much easier to handle. You'll notice that all major network data models (XML, JSON) are acyclic, as are SQL and NoSQL databases alike. Cyclic databases exist [1] [2], but they're very much the exception.

And of course, acyclic data models mean you don't need a tracing garbage collector. Are doubly linked lists, etc, really worth a tracing garbage collector? Anyway, in a language/OS where transmitting and persisting state are core features, acyclic data seems like a pretty easy choice.

[1] https://en.wikipedia.org/wiki/Network_model

[2] https://en.wikipedia.org/wiki/Object_database

Abstractly, a noun as defined is a tree and trees don't have cycles. So there! :-)

You shouldn't require the reader to make this inference. The 2nd order implications are far less obvious to the reader than to the writer. A good writer should never assume the reader will get it on the 1st mention. In fact, I'd hazard a guess that most of the time, when you've made such inferences while reading, that an author has subtly primed you to do so beforehand.

(comment deleted)
It looks like urbit isn't really “public” yet; they claim to be in “semi-closed alpha” still.
It's actually more open than it looks. The code (https://github.com/urbit/urbit) is available to anyone interested. Anyone can build and run a comet.
Can you explain what a comet is, exactly? If I were to setup my own "comet" would that mean that I'd have my own network, detached from the one the urbit developers are a part of?

I'm interested in playing with urbit, but I'd also like to try it with some people to talk to, and not on my own island.

A comet is an Urbit with a self-signed certificate. You're a part of the same network that everyone else is on. Comets are one of 2^128 possible addresses, so they're anonymous and disposable. For the time being it's possible to use a comet to come and talk with everyone. We're also glad to answer questions over email.
Thanks for explaining this. So invites are only for "planet" names?

I'm still having difficulties understanding the reasoning behind the different levels, explained in the white paper.

I've got it running, thanks for letting me know that I could just compile it and spin it up. I've been getting the newsletters for urbit and have been wanting to play with it. I didn't realize I could do that without an invite.

All of this can be neatly summarized as "a noun is either an unsigned integer or a pair of two nouns". The authors apparently love to give things funny names and weird syntax, but there's nothing here that Lisp didn't do 50 years ago. Let's see what happens in chapter 1, though.
Yes! Although there are some things Lisp did 50 years ago, that aren't here. But this is less an improvement on Lisp, than a tribute to Lisp.

Chapter 1 should remind you less of Lisp. Although I do expect to hear "that's just caddadr"...

So basically this is a lot of syntax to add something resembling a type-system on top of a fundamentally un-typed language?
Yeah, that's pretty accurate.
Possibly a silly question: did you consider using the complex integers (either as a + bi, or as (r, 2*pi/T)) instead of the natural numbers as the basis for Nock?
Cells make you think of a+bi a little bit, but no.

(Actually I was very pleased when I realized that signed integers have no place in a fundamental interpreter.)

Two pieces of feedback:

First, at the end of this I'm still rather confused about what molds are for, and it's implied that all will be explained in the next chapter. This is a tutorial, not the next great American novel: do not end chapters on cliffhangers! After finishing a chapter, the reader should be able to go back and fully understand every concept introduced in the text. Either expand this chapter to include a complete explanation of molds, or move molds into a separate chapter.

Two, you include this example of a mold:

++ span

  $%  [%atom p=@tas]

      [%cell p=span q=span]

      [%cube p=* q=span]

  ==
This is chapter 0 of a tutorial on Hoon: do not just include Hoon syntax with no explanation! I have some idea of what this function does from context clues, but I have no way of knowing if my intuition is correct. This is why pseudocode was created: use it! Here's my best guess of what that function does:

  function span_mold(noun n)

    if n is of the form (p,q)

        return [%cell span_mold(p) span_mold(q)]

    else if n is of the form @a

        return [%atom %a]

    else if n is of the form %b

        return [%cube b span(b)]
Is that right? I have no clue!
It is right! You do have clue!

This is excellent feedback. Normally I'd agree with it. Ideally, this will be the last cliffhanger in the tutorial...

(comment deleted)
"FP for street programmers" sounds like a win.

Unfortunately I couldn't really understand anything else. My Lisp is rusty, but dotted pairs aren't something I usually have so much trouble following.

Is there a tutorial that's mostly code examples and not so much text?

The next tutorial is a lot less wordy, and should hopefully shed a good amount of light backward on this one.
Some languages are so disconnected from programmer needs and real life usefulness that they make me say "I'd rather learn assembler".
How in the world do typeclasses work in Hoon?
I don't think Hoon's type system actually has a typeclass analogue. As far as I could tell, what it has is something like generics, but in a world where everything is ultimately a noun. IIRC, a previous iteration of the docs explained that "wet gates" (generic functions) are actually required to compile down to the same Nock when "instantiated" at a particular argument, modulo dead code elimination. Didn't look much like ad-hoc polymorphism to me.

A good example is Hoon's maps[1]. They're parameterized on type, but I'm pretty sure those types can't affect the runtime behavior by, say, specifying their own hash or comparison function. Instead, the map implementation[2] hardwires a couple of specific comparison functions[3] that effectively toss the type information and work in terms of the raw underlying nouns.

It is kind of weird that every type carves out a subset of nouns, even function types (or function "molds" or "spans" or whatever... I'll stop calling them types when the Urbit people stop calling it a type system). Hoon's C-flavor really shows when it makes the likes of strlen((char*)strlen) expressible in a purely functional way.

[1]: https://github.com/urbit/urbit/blob/7186219/urb/zod/arvo/hoo...

[2]: https://github.com/urbit/urbit/blob/7186219/urb/zod/arvo/hoo...

[3]: https://github.com/urbit/urbit/blob/7186219/urb/zod/arvo/hoo...