Re: the jargon, there's a technical reason for it. Urbit atoms—which are the Nock runtime's only non-product "type"—are a lot of things, but they're basically a union-type between a bit-string and a bignum (i.e. a way to talk about a set of packed bits and an arbitrary-length number interchangeably.)

Bignums themselves are somewhat expensive data structures to pass around, so runtimes that use them usually don't use them for everything; instead, they have an Integer type that has Bignum (data-structure on the stack, or pointer to data structure on the heap) and Fixnum (regular integer machine-register) implementations, and write a glue layer to treat the two interchangeably.

So, you've got a runtime where your only "type" is an integer, but it breaks down into "cheap integers" and "expensive integers."

Now, in most dynamic-language runtimes (i.e. runtimes where type-tagging and pattern-matching doesn't all happen at compile-time), you want a type that "represents itself" to use for type-tagging, dictionary keys, function references, etc. This is usually a Symbol or Interned String (or Atom!) type, with the runtime keeping a symbol table mapping fixnums ('cheap integers') to strings (which are effectively, in Nock, 'expensive integers'). You need this table, because dynamic language functions manipulate/build/pass around/compare a lot of symbols, and the interpreter would be really slow if you were passing and comparing 'expensive integers' by value.

But Nock can't really keep a symbol-table, because every "atom" ID embedded in a Nock program is a universal ID, a handle any other Nock runtime might be passed and attempt to manipulate. (And you can't even use a global symbol-table that uses hashing—i.e. a DHT—because Nock doesn't differentiate the "Symbol" atoms from any other atoms, so you'd have to treat every atom the same and hash them all—and, even pretending that's cheap, you're eventually going to get collisions that way.) The only symbol table that works in a globally-distributed system is one that maps bitstrings to themselves. And that's as good as no symbol table at all.

So what do you do when you can't make your strings ('expensive integers') into interned strings ('cheap integers')? Just use the cheap integers from the start! In Nock, this means that all the runtime-defined symbols—all the atoms in the language and the stdlib—are bitstrings that are short enough to be encoded directly as fixnums. Since Nock has the requirement of running on 32-bit machines, that means 32-bit bitstrings: four-ASCII-character strings. Thus most of the jargon.

Do note, it's not required to use four-letter names for "symbols" you manipulate in the Nock runtime; but doing otherwise means passing around 'expensive' handles instead of 'cheap' ones.

---

I find Urbit's solution here interesting—but they probably chose it for purity's sake.

I would like to contrast it to the implementation in Erlang's BEAM runtime (which also, coincidentally, has 'atoms', but where these are simply its particular symbol type):

• an atom table is kept by each "node", with no need for the atoms in each node to have identical mappings;

• atom IDs are mapped back to their original bitstrings when serializing any data structure including them, even if just for internal persistence;

• each distributed-RPC connection between nodes is stateful, keeping its own atom table (kept synchronized on both sides by additional messages) as a cache to speed up communication between nodes. This can be seen to be similar to a streaming compression algorithm's Huffman tree cache, but it's interesting to look at it as the connection being its own virtual 'node' that has this particular atom table, which both nodes are then modelling and translating to/from.