It’s been a while since I’ve posted a chapter of this Urbit book! So here’s another one. I’ll post just one more. And then to read the last few chapters you’ll have to buy a copy in print or e-book form.
This chapter gets pretty technical, but I tried to make the rationale behind why Urbit is the way it is it as clear and fun to read as possible. You can judge for yourself whether I succeeded. If you have any questions, please feel free to shoot me an email or post a comment.
Now that the digital edition of the Mars Review is now running entirely on Urbit, I can separate this Substack. I won’t be writing specifically about Urbit or the Mars Review here. This will be a general interest publication on technology, religion, literature, and New York. I’ll keep the title “Machine War.” That covers a lot of ground. See for instance the first reference the OED gives for the word machine: “The hole machyne of this world is divided in .2. parte. That is to saye, in the celestiall and into the elementall regions” (J. Schäfer, 1545).
You can subscribe to the Mars Review by clicking on any article here and scrolling to the bottom. To stay updated about great events coming up, follow us on Eventbrite and Instagram.
Alright, here’s Chapter 8, beginning with a quotation from a lovely modernist poem:
A Software Psychoanalyst
I was the world in which I walked, and what I saw
Or heard or felt came not but from myself;
And there I found myself more truly and more strange.
—Final Stanza of Wallace Stevens's “Tea at the Palaz of Hoon”
Even a non-architect will have at least a vague sense of why an architect might build a building with steel instead of brick, or with wood instead of glass; even a non-filmmaker can guess at a filmmaker's aim in shooting in black-and-white instead of color, or in casting a certain actor in a certain role; and these days we're increasingly sophisticated enough to know why our news sources choose to associate certain images or certain phrases with certain ideas, or why they might feature certain stories at all.
And yet, ask a non-programmer why certain choices have been made in the composition of the programming languages that act as the lifeblood of our networked existence—and you’ll be lucky if you get anything beyond a blank stare. In the realm of programming languages—and the operating systems, web stacks, and mobile apps that flow out from them—we are like our distant ancestors who took for gospel that every event that rolled through their lives was the will of the gods. The fire that we started in order to cook our mutton ended up burning down our hut? It happened because it was the will of the gods. The whole tribe got sick after drinking from the river down yonder? It happened because it was the will of the gods. These are also the thought patterns of paranoiacs and the catatonically depressed. Those of us who are software users—which is to say, everyone in the developed world—might be well served by a software psychoanalyst. This modern-day sage would help us to see the history of our digital lives as a series of choices.
Urbit is written in a unique and (to some) disorienting programming language called Hoon. To understand the choices that led to the composition of Hoon (or of any other programming language), it might be useful to consider what computer programming actually is.
In the foreword to the second edition of the canonical text Structure and Interpretation of Computer Programs, which was taught to entry level computer science students at MIT for the better part of two decades, authors Harold Abelson, Gerald Sussman and Julie Sussman provide a provocative answer to this question. [1](Abelson, Sussman and Sussman, Structure and Interpretation of Computer Programs, 2nd Ed., xxiii)
Underlying our approach to this subject [computer programming] is our conviction that “computer science” is not a science and that its significance has little to do with computers. The computer revolution is a revolution in the way we think and in the way we express what we think. The essence of this change is the emergence of what might best be called procedural epistemology—the study of the structure of knowledge from an imperative point of view, as opposed to the more declarative point of view taken by classical mathematical subjects. Mathematics provides a framework for dealing precisely with notions of “what is.” Computation provides a framework for dealing precisely with notions of “how to.”
This is to say, a programming language is something that is constructed to do things. In this it differs from subjects like mathematics or philosophy, or even, perhaps with human languages. (One major linguistic theory proposes that human languages emerged as an outgrowth of abstract thought, not for the humdrum purposes of communication with other humans.) [2](Chomsky and Berwick, Why Only Us?) All aspects of a computer program, on the other hand, are in some way tied to the results that will be reached by running that program. A computer program is never something static to be contemplated for its beauty (although it may be beautiful underneath the hood); it is something dynamic to be iterated over time. A computer thus has the same preferences as the 19th century Missouri Congressman William D. Vandiver, who gave that state its nickname when he declared, "Frothy eloquence neither convinces nor satisfies me. I'm from Missouri. You've got to show me."[3] And differences in programming languages will emerge from differing philosophies about how best to do things (i.e. how to structure algorithmic procedures), or, perhaps, from differing philosophies about which things are worth doing and which things aren't.
Functional Breathing
What then, are the ideas about how to do things and which things are worth doing which went into the construction of Hoon? For the technically knowledgeable, Urbit Foundtion CTO Ted Blackman has provided a thorough explanation in the urbit.org blog post "Why Hoon?".[4] The docs at urbit.org also provide a full course for learning Hoon and a glossary definition.[5] Here is the first line of the definition: "Hoon is a strict, higher-order typed functional programming language that compiles itself to Nock." I discussed Nock a bit in the previous chapter, and I'll cover "strict, higher-order typed" in the next one. For now let's focus on "functional."
Functional programming languages constitute a class of programming languages, typically in opposition to the class of imperative programming languages. Yet the term functional programming language has the potential to be misleading. After all, a function is simply something that takes an input and produces an output. Any programming language, functional or not, is going to be chock-full of functions. Moreover, the terms functional language and its counter-category, imperative language, were created after the fact to describe programming language paradigms that already existed. That is, when John McCarthy invented the first functional programming language, LISP, he didn’t call it a "functional programming language." He was just inventing a language that would be able to do the things he wanted it to do, with the most sensible parameters he could conceive. It should also be noted that whether a language is functional or not is a matter of degree. There’s room for argument about whether certain languages can be classified as functional or not, and their performance as functional languages might depend on the importation of certain libraries (tranches of code that give the coder new powers, as it were).
With that out of the way, what is a functional programming language? In what sense is Hoon a functional language, and why does it matter?
In a 2015 post “What is Functional Programming?” software developer Kris Jenkins gives a straightforward answer to this question—an answer meant for “jobbing programmers” as opposed to academic theorists.[6] His answer?
Functional programming is about writing pure functions, about removing hidden inputs and outputs as far as we can, so that as much of our code as possible just describes a relationship between inputs and outputs.
This might seem a little odd at first. Doesn’t all code just describe relationships between inputs and outputs? If not, what else would code do? Certainly that's all that a computer in its most basic iteration does. Take for instance the simplest computer around: an abacus. Let's say your abacus has ten beads per row and you want to subtract seven. Seven is your input, the process of sliding the beads over to the right is the execution of the program, and the number of remaining beads on the left (three) is your output. All of this is perfectly above-board.
Jenkins’s observation is that actually a fair amount of computer code has hidden inputs and hidden outputs. The computer programming term of art for these inputs and outputs is side effects, although Jenkins is careful to note that side causes are as much of an issue as side effects. To make his point, Jenkins shows an example of some computer code that does not seem to have any inputs or outputs at all. On the surface of it, this set of instructions does not depend on anything and it does not produce any effects. No actual functions are called in the code. And yet running the code under different conditions would show that the code does actually depend on things, and it does produce effects. It's as though the abacus were shifting beads around, but the operator of the abacus was blindfolded so that he could not see the start point or the endpoint of this shifting operation.
To put it simply: Any computer code not only depends on the inputs it is given; it also depends on the state of the machine. Let's return once more to the abacus. Obviously, anyone intelligent enough to use an abacus is intelligent enough to know that his or her calculations will only come out correctly if the beads of the abacus are reset before performing the next operation. That is, if I have ten beads in a row and subtract three in order to find out the difference, it is obvious that I cannot subtract from those seven remaining beads in order to obtain the difference of 10 - 2. I must reset the total number of beads back to ten.
Another potentially useful (if not rigorously exact) analogy for thinking about this is to imagine you’re designing a human being. The first obvious thing you might want to program this human to do is to breathe. The human needs to breathe whether he is in Amsterdam or Zanzibar, whether 2,000 feet above sea level or 2,000 feet below. So from a certain perspective, it seems that breathe has no inputs and no outputs. It’s simply something that gets done. But there are going to be exceptions to this rule. What if your human is under water, or in a room filled with carbon monoxide? These are factors that are going to affect the health and happiness of your human when he tries to breathe. In which case, you do not want these factors to be hidden within the breathe operation. Your human might be sucking seawater when he’s supposed to be holding his breath, or turning purple on an otherwise idyllic picnic.
Now, you can write some code that instructs your human to cease breathing temporarily if he's swimming underwater, or if he's in a room with a noxious smell. But in that case, you're definitely going to want to make these conditions explicit in the function. Even though in the vast majority of cases, you want your human to breathe instead of holding his breath, you still want your function to look like If on land, and if no carbon monoxide, if no noxious smell, etc. etc., then breathe. This would be in contradistinction to something more like Assuming the state of the human is propitious for breathing, let him breathe. In the latter case, the human's state is not an explicit part of the breathe function. Maybe the programmer can still handle keeping his human alive, but this will entail a lot of hidden complexity managing his state; and any errors stemming from the hidden complexity of this program might well prove to be fatal.
Tea at the Palaz
Although there can be arguments over the exact definition of "functional" or "purely functional," one can certainly call Urbit's Hoon a functional language in Jenkins's sense: that is, its aim is to do away with hidden side-causes and side-effects. Recall from Chapter 1 of this book that the "Martian software" Urbit intends to emulate is tiny and diamond-perfect. Complexity should be at an absolute minimum. And to bring complexity to a minimum, you absolutely do not want code that’s rife with hidden causes and hidden effects. Thus in order to create tiny and diamond-perfect code, you want to use functional programming language principles.
The important point is that Hoon uses principles of explicitness in order to reduce complexity. One of the aims of Hoon is that any Urbit user should be able to fully understand the processes going on beneath the hood. Another is that Hoon code should be fully deterministic; that is, the behavior of one's Urbit computer should always be determined solely by the code that it's running, and not by the hardware or the underlying operating system beneath Urbit. Both of these aims depend on explicitness. As Blackman puts it in “Why Hoon?”, “One of the best ways to reduce software complexity is to restrict oneself to pure mathematical functions—no side effects, no implicit arguments.” So if you were using Hoon to program your human to breathe, you would not have to do any digging to find out whether he is in a situation where he ought to be breathing or not. This information would be explicit.
But another, related aspect of Hoon gives an even better sense of its devotion to rooting out implicit arguments, and this aspect should also give the reader a better sense of Hoon's uniqueness. This is the fact that Hoon is a “subject-oriented language.” Unlike “functional language,” “subject-oriented language” is not a term in common use among everyday computer programmers. To get a sense of what it means in the context of Urbit and Hoon, we can turn to the official Urbit Hoon tutorial documents: "every Hoon expression is evaluated relative to some subject . . . [and] the subject defines the environment in which a Hoon expression is evaluated."8. This sounds rather abstract, but actually the concept can be understood pretty easily.
In Hoon, when you define what is normally called a variable (but which is called a "face" in Hoon), you are merely altering your computing environment, and whatever inputs you give to the computer will then be evaluated in terms of that altered environment. To put it as simply as possible, naming a variable (saying that b=5, for example) changes the environment of your computer in an explicit way. And any future calculations you wish to do on your Urbit computer will invoke that newly altered computing environment in an explicit way. This may be illustrated by turning once more to the abacus analogy. You can imagine you have an abacus with ten rows of ten blue beads, making for one hundred blue beads total. And then you can imagine you've painted the first bead on the third row red. Now you've got a different abacus than the one you had before. As a human, it's very easy for you to recognize this information and go about your business. You can perform operations on the fourth row or the fifth row or whatever other row, without letting the fact that the third row now has one red bead distract you. However, the fact is, you're no longer performing operations on abacus with 100 blue beads. You're performing operations on abacus with 99 blue beads and one red one. The environment has changed. And this is true for more complex modern computers, just as it is true for the abacus.
The value-add a Hoon-style abacus would bring is this: Whenever you are performing any kind of function in Hoon, the change in the environment is explicitly stated. So, if you were performing 10 - 9 on the eighth row of a Hoon-stlye abacus, the operation would look like this: given an abacus with 100 beads, where the first bead on the left in the third row is red instead of blue, slide nine beads to the right on the eighth row. It might not seem that the information about that one red bead is relevant; but making all this information explicit is precisely the sort of maneuver that prevents hidden side-causes and hidden-side effects.
The point can also be illustrated with an actual example from Hoon. Entering the command `(add 2 2)` in your Urbit programming environment will, as one might guess, give the result `4`. On one level of inspection, all you're doing here is adding two and two. But a closer inspection of what's going on will reveal Hoon's unique structure. All Hoon expressions, including our simple addition of two and two, are evaluated relative to a subject. The subject, in the case of `(add 2 2)`, is what I've been calling the "computing environment" or "environment": it is a tranche of computer code taking into account the temporal state of your Urbit computer, the ownership details of your Urbit computer (i.e. what is its unique identity?), as well as other 'entropic' code to keep your computer cryptographically safe, and "standard library" code which facilitates the performance of functions like add. What your Urbit computer is hearing when you tell it `(add 2 2)` is "find the command add within the standard library of Hoon functions, apply that command to the inputs 2 and 2, and then return the entire environment, given that 2 and 2 have now been added together. That is to say, there are no isolated events within Urbit. Every operation you run is an alteration of the Urbit computer you're running it on, and the result of every operation is the altered state of your Urbit computer. There's not outside or inside. Hoon is both the operator and the operand of its operations.
For a computer programmer, these are not idle, theoretical concerns. And in the case of Urbit/Hoon, they're choices made with very specific aims in mind. Those aims are long-term durability (so that in 100 years the code will run the same way it runs today), simplicity (so that there are no hidden side effects or side-causes in the code), and independence (Hoon expressions will not depend on the changing software landscape that surrounds it, and thus anyone who owns an Urbit computer owns it fully). Programmers coming to Hoon from more normative paradigms have often found it strange; and, strangeness being relative to what one is used to, Hoon no doubt is strange. It has been conjectured that Yarvin, a poetry fan and a composer of verse himself, named the language after the Wallace Stevens poem "Tea at the Palaz of Hoon," excerpted at the beginning of this chapter, which indeed ends with the line "And there I found myself more truly and more strange."9
And yet the more relevant line from the poem may be "I was the world in which I walked." Certainly there are parallels between this line and the behavior of Hoon: as stated above, a Hoon expression is never separate from the context to which it is applied. A dramatic way to conceptualize this is to recall the climax of The Matrix, in which the protagonist Neo recognizes that, when in the Matrix, he is made up of the exact same substance as his surroundings. At a fundamental level there is no difference between himself and the world around him. Like Neo in the matrix, any computer program is made up of the same stuff as its environment: binary digits. And, as with Neo, certain new powers may come to a computer that makes this similarity explicit.