17 comments

[ 0.22 ms ] story [ 49.8 ms ] thread
There's something so principled about the Urbit team. Everything from their website to the font :)
Since I picked the font, I'll take that as a compliment! Principled, I'll take. We try our best not to be stubborn.
Is Urbit some kind of performance art project? Every time I read about it, it feels like I'm reading Edward Lear.

Edit: Example from Reddit: "sensible reason there are so many four-letter words is that they are symbols in the kernel, and four-letter symbols fit into 31 bits, which lets us use machine ints as an interpreter optimization rather than calling out to GMP."

Sensible?! Hahahaha! This has to be comedy. They are bigint-encoding identifiers? There is no sane reason to do this.

OK, I'm just going to go through the website here:

"If Bitcoin is money and Ethereum is law, Urbit is land."

Wow, how expositional.

"A 32-bit ship (like ~firbyr-napbes) is called a "planet." A 16-bit ship (like ~pollev) is a "star." An 8-bit ship (like ~mun) is a "galaxy.""

Edward Lear on subnets?

"Much as all real-estate titles in England trace back to William the Conqueror, all certificates in Urbit trace back to ~zod."

All hail!

"As an Urbit user, these basics are all you need to know."

Really? Because I still have no idea what the hell is supposed to be going on here.

"A principle called "Zooko’s triangle" says that a practical identity system can achieve any two of these goals."

Disproved by Namecoin, FWIW.

"For more on Urbit's governance design, see our beliefs and principles and our interim constitution."

Oh great, so this "personal server" project (as described on the front page) has a constitution and a statement of principles?

Seriously, the only way I can interpret this is as some sort of abstract absurdist comedy project.

And yet, we're completely serious.
I've seen you guys comment to this effect several times, but the most plausible explanation I can come up with is that this is a Nathan Fielder-style performance comedy. You insistently play the role of someone who is totally invested in their clearly nonsensical idea, and you take advantage of the fact that social pressure will cause people to go along with it.
When you look at it that way, it's pretty funny! But, the truth is, I'm actually sitting in an office working on Urbit. I'd invite you to come by for coffee or something, but the internet tells me you're in Austin. Would Skype count as proof?
I'm in CA right now. I'll send you an email. I'd be happy to be proven wrong and that Urbit is totally reasonable.
Let the HN record show: wyager and I are going to meet up at some point. I may or may not be able to convince him that Urbit is real — but civility prevails nonetheless!
> Sensible?! Hahahaha! This has to be comedy. They are bigint-encoding identifiers? There is no sane reason to do this.

This fixed machine-width type of character encoding isn't unheard of. For example, Windows uses tags of this size to identify chunks of memory associated with drivers: https://msdn.microsoft.com/en-us/library/windows/hardware/ff...

Yes, but it's terrible design to use this in a dynamic interpreter. You can either use a proper text type or use identifier substitution.
> They are bigint-encoding identifiers? There is no sane reason to do this.

There needn't be any "encoding" really. Modern CPUs have a 32-bit compare instruction. The CPU doesn't understand types as a compiler does; it will accept any aligned series of 4 bytes as a comparand. Some implementations of strcmp use that. Forcing the identifiers to 4 bytes eliminates some if-else edge casing before the compare, which might speed things up.

Disclaimer: I don't know anything about urbit. I agree such an optimization is surprising. The more normal pattern is to convert human-readable symbols to integers early, thus avoiding string comparison in performance-critical sections.

cyarvin: As Dijkstra said: "the purpose of abstraction is not to be vague, but to create a new semantic level in which one can be absolutely precise."

So, for instance, Urbit has three abstract concepts matching the word "type", as normally used: "span" (set of nouns), "mold" (constructor), and "mark" (external/MIME type). It's not just that it's confusing to assign the word "type" arbitrarily to one of these (it will still be used indiscriminately for all, and is).

Yes, it would be confusing to assign the word "type" to these concepts. Which is why we don't do that! We use "constructor" for an operation that instantiates a type, "external representation" ("printed rep", etc) for how we encoded it for storage and transport, and "domain" for the set of values belonging to a data type.

"Constructor" already got used by C++ for something different. A Hoon mold is more like a C++ struct than a C++ constructor.

And if you saw a file of Hoon molds, in normal programmer English, you'd say, "these are type declarations." I might even say it myself.

As for the external representation -- it's a name for a file containing a Hoon mold. Actually orthogonal to serialization format, as all nouns are serialized the same way. It really is more like a MIME type, if a MIME type was a function you could run. There's no word for this.

There is a common programming term for a set of values. The word is "type." "Domain" is a little bit odd, because we usually compute the "type" (span) as the range of an expression...

Bottom line is, this stuff is hard!

> "Constructor" already got used by C++ for something different.

Constructor was used (long) before C++! For instance, the "cons" function in Lisp stands for "construct", and speaking of which, ANSI Lisp even has "boa constructors" (by order of arguments). We are used to having terms like "constructor", "array" or "variable" have different shades in different languages. If I reach for Awk to munge some text fields, I'm not suddenly confused because its "arrays" are not like C arrays or Fortran arrays.

The same terms being used is actually helpful, because the terms form the pivots in conceptual mappings between languages, and comparisons. Even if two languages have different notions of what is a "variable", they are used in similar ways. It's easier to hang different shades of meaning on the same word than to memorize some made up new word. Even something quite different, like placeholders in pattern matching or predicate logic, can still be called "variable" without causing confusion.

Think about parameters/arguments. They can be by value, by name, by reference, or normal order (lazy). Using a different word instead of "parameter" for all these would be counterproductive, and make it harder to speak comparatively.

Bottom line, people aren't complete idiots.

"The Flunk language has flurbles, which are like C++ variables, but not exactly, so we use a different word. Unlike variables denote locations which do not have a type and can store any value." (Gee, that's just like "variables" in some existing languages that are not C++.)

Maybe that's the point. If you implement, say, arrays very badly, and call them arrays, then everyone can easily criticize you in very direct terms: "Flunk has arrays, but they are broken compared to Python arrays, or even C arrays and Fortran arrays". The left-most "arrays" in that sentence can be a straightforward, blue-underlined link directly to the Flunk documentation which describes arrays. But if you call then batteries, then such a comparison gets confounded with distracting explanations. It's harder to argue that Flunk has broken arrays, when it has something called batteries that hold cells which have polarity and whatnot. First you have to argue that they really are just arrays under a different name, then you have to argue why they are bad compared to arrays, and why that still matters in the context where they are batteries. By the time you're done, you look stupider than Flunk, and more negative than the (-) end of a Flunk battery.
You're very eloquent and also learned. But for every programmer who knows what an ANSI Lisp "boa constructor" is, there are 100 who know what a C++ constructor is. And it's an 11-letter word that means something completely different in a completely different language.

But maybe it's worth it to stop the middlebrow dismissals! Our grandparents went to the moon. We complain when we have to learn a slightly new idea, and faint like Victorians contemplating legs if it has a new name.

"Argument" is another good one. Hoon is a single-argument language that favors tuples, not currying. So an add function, (add a b), is actually (add [a b]), or in Lisp (add (cons a b)).

In Hoon, you can call a and b "arguments" all day long. It's an informal word. But [a b] is "the sample." You can see the chaos that would result if we called it "the argument" or "the parameter"...

cyarvin: But more broadly, it's very difficult to invent a system like this if you need to commit to reusing the abstractions of 20th-century programming. A reused term comes with all kinds of assumptions. Sure, in some cases it's possible to retcon the system back into "normal" language, but a mismatch should be expected. A good example is the homoiconic state of Hoon/Nock. In basically all other languages, even including Lisp, the set of data available to an expression is not a first-class value in the language -- it's a "scope" or "environment" or some other secret mystery meat. In both Hoon and Nock it's a single, ordinary value -- the subject.

In mainstream Lisp dialects, that first class value exists: it is the lexical closure. You might not like the indirect "API" that it gives you to the environment (namely a function, which you have to program yourself to do what you want), but that's what it is. Your function can contain logic to inspect variables:

  ;; CL
  (let ((a 1) (b 2) (c 3))
    (lambda (which-var)
      (ecase which-var (a a) (b b) (c c))))
If we bind this to f, and (funcall f 'a) we get 1, and so on; we've given the mystery meat object a method for looking at some of the accessible values.

There may be some object representing an actual environment. That is very dialect specific. Common Lisp has macro-time environments, so a macro can manually expand a sub-macro in the correct scope.

In the TXR Lisp dialect, you can do this:

  1> (make-env)
  #<environment: b7653e3c>
  2> (env-vbind *1 'a 3)
  (a . 3)
  3> (env-vbind *1 'b 2)
  (b . 2)
  4> (eval '(+ a b))
  ** (expr-4:1) unbound variable a
  5> (eval '(+ a b) *1) ;; pass in the env
  5
In principle, any dialect could have this. I think it's just not considered that useful in the Lisp world. For one thing, compiled code may want to use an optimized representation of the environment. A compile function could take an env also, but that env would likely be copied and transformed into something else (that no longer shares bindings with the original env object).

Environments as first-class values are bit of an antithesis to the compiling of efficient lexical scopes. This is not out of ignorance; the Lisp world made that choice way back when for some good reasons. (Those reasons are neither forgotten, nor unquestioned).