We are often asked why Urbit was written in a new language, rather than some existing language like Haskell or C. This document provides the high-level rationale for that decision, and also covers some of the features of the language that set it apart from others. If you're looking to learn Hoon, check out our tutorial series called Hoon School below.

What can Hoon do that other languages can't?

The short answer is: implement a purely functional operating system. Try to do this in a principled way in Haskell, and the problems you'll run into will make design decisions in Hoon and Nock make a lot more sense.

In particular, the problems Hoon solves that aren't solved by other functional languages are:

What is Hoon good at?

Hoon is mostly good at compiling and running other Hoon code. Urbit consists of many layers of bootstrapping. Several of these layers lean heavily on this feature, including the Gall application runner, the Ford build system, the Dojo shell, and the Arvo kernel itself. Even Urbit's chat application lets you run Hoon expressions and share the results with your friends.

Why did we write the OS in Hoon?

The chain of reasoning goes something like this:

Software complexity leads to monopolies and lack of individual digital sovereignty, in addition to bugs and security vulnerabilities. One of the best ways to reduce software complexity is to restrict oneself to pure mathematical functions — no side effects, no implicit arguments. This makes the system deterministic. So we want a deterministic, functional operating system for individuals to run.

This operating system should also be axiomatic; we don't want it to depend on various idiosyncrasies of whatever the current hardware is like. Hardware changes over time, and we want people to be able to pass their computers on to their grandchildren, so we should have a virtual machine that runs this functional operating system.

As hardware changes, people need to move their virtual machines to new hosts. This means there needs to be a standard way to serialize and deserialize the VM state at any time. The easiest way to do this is by storing an event log, just like a database, and writing each event to that before emitting effects caused by it. Since we also need state snapshots, every piece of data in the system, including runtime state, needs to be serializable to a standardized format.

Because the VM will likely move from host to host many times, it needs to be tractable to actually implement a VM correctly. This means the system specification needs to be simple enough that it's clear whether a VM is in fact correct. The x86_64 instruction set that runs most servers has somewhere between 1300 and 4000 opcodes, depending on how you count. Nock has 12.

Since Urbit is an operating system, its main purpose is to load and run programs on behalf of the user. This means the system needs to be really good at hot code reloading, self-hosting, metacircularity, and virtualization.

There aren't any other languages out there that are purely functional, purely axiomatic, performant enough for practical personal use, universally serializable, and good at runtime metaprogramming. Nock is Urbit's solution to these design constraints. Some Lisps come close to meeting these criteria — and Nock is very Lisp-like — but no practical Lisp dialects are nearly as pure or axiomatic as Nock.

What is special about Hoon?

It's a purely functional systems language. Calling it a functional analog of C is not too far off in several ways. Almost all code throughout Urbit's kernelspace and userspace is written in Hoon.

What properties does Hoon have? What type of language is it?

Hoon is a statically typed, purely functional, strictly evaluated programming language.

Hoon and Nock have several unusual properties:

Why is Hoon the way it is?

Minimalism, mostly.

Urbit's principled minimalism simplifies all kinds of things at many layers of the stack; for example, Urbit's linker, which is part of the Ford build system, just conses together multiple libraries into a tuple to form the compile-time environment for a source file. Universal serialization means we can safely send arbitrary pieces of data to apps on other ships without any more work than sending them to a local app. Using an app called Aquarium that's about three hundred lines of code, Arvo can run a whole fleet of other Arvos inside itself at full speed, just like Docker, and perform a suite of deterministic end-to-end tests on the OS as a whole.

Even Hoon's seemingly baroque syntax is extremely regular and an unusually thin layer over the abstract syntax tree. It's designed to be a power tool; learning the syntax takes some time, but you only have to learn it once, and then it's not hard to read. It's like an English speaker learning Hiragana or Cyrillic. This heaping spoonful of syntactic sugar (along with jets for performance) is enough to take Nock from a Turing tarpit to a practical, ergonomic programming tool.

Subject orientation in Nock and Hoon stems partly from minimalism (there's just one subject, which serves as state, lexical scope, environment, and function argument), partly from a desire to simplify compilation (the type of the subject is a full specification of the compile-time environment for a source file), and partly in order to give the language a more imperative feel.

You program Hoon as if you have a mutable environment, but you're embedded in a purely functional, immutable context. While Hoon is a purely functional language, many of its runes create a mutant copy of the subject for manipulation by future runes (similar to Forth's stack operations), which makes it feel more like an expression is "doing something" rather than just calculating something.

Everything about a scope, including name bindings, aliases, and docstrings, is stored in the subject's type. This allows Hoon's compilation discipline to be similarly minimal: the compiler is a function from subject type and Hoon source to product type and compiled Nock. Running this Nock against a value of the subject type produces a vase of the result. It's hard to imagine a more streamlined formalism for compilation.

The compilation discipline gets applied recursively to build the layers of the Arvo stack. The Arvo kernel is compiled using the Hoon compiler as subject, the Zuse standard library is compiled using the Arvo kernel as the subject, and apps and vanes (kernel modules) are compiled using Zuse as the subject.

The promise of Urbit lies in its reimagination of the digital world using components that are as constrained and limited as possible. By adhering firmly to principle and doubling down on minimalism at every turn, we get an OS that provides far stronger guarantees than Unix with a thousand times less code. Given the complexity of modern software, this is what's required to put personal computing back into the hands of people.