Kernels#
A kernel is an executable Nock noun that has computable arms at certain axes of its core. These arms can be invoked by a runtime to perform computations, manage state, and interact with the outside world. A state is maintained with the kernel (in practice, the core type is what we call a “door”, or core with a sample), and the kernel’s arms can read from and write to this state as needed.
The most common kernel shape is a core with arms at certain fixed axes:
+4is the+loadarm, which handles loading and upgrading the kernel state. It accepts the current state type and returns a state of the kernel’s most up-to-date type.+10is the+wisharm, which evaluates Hoon expressions against the current state. It accepts a core to evaluate against the kernel’s state (this varies by runtime). One use for+wishis to read out the version of a standard library for compatibility checks on upgrades.+22is the+peekarm, which provides read-only access to the kernel state. It accepts apath(list of text atoms) and returns the value corresponding to that path within the kernel’s state.+23is the+pokearm, which processes state-altering requests. It accepts acause(formatted noun) and returns a cell of effects and the new kernel state.
The arm order arises naturally from the +$set type used in Hoon, which orders the arms according to a hash of each name. Notice that they are all in the battery of the core, nested under axis +2.
+peek Read-Only Access#
+22/+peek accepts a path, or list of text atoms, (list knot). Its invocation from a kernel results in a (unit (unit noun)), where:
~means a result is not currently available (block).[~ ~]means a result will never become available (halt).[~ ~ noun]means a successful result. The caller unwraps it twice (the tail of the tail) to get the raw noun.
Because peeks are read-only, they can be cached for efficiency. Peeks are often called “scries” in Urbit terminology.
+poke State-Altering Requests#
+23/+poke accepts a cause or move and processes them; this is the only arm that actually alters Arvo’s state. It results in a [(list effects) new-state].
The runtime is generally responsible to queue causes and call +poke serially, ensuring that state changes are atomic and consistent. The kernel processes each cause in order, producing a new state each time.
A Simple Kernel#
The simplest possible kernel is one that does nothing; we will write one that simply returns its state unchanged for +load and +poke, crashes on +wish, and returns ~/0 for +peek.
[8 [1 0][0 S] [1 [8 [1 0] [1 0 6] 0 1]+load, axis 4: a gate whose arm [0 6] returns its sample, the old state [0 0]+wish, axis 10: [0 0] crashes whenever it runs [8 [1 0] [1 1 0] 0 1]+peek, axis 22: a gate whose arm [1 0] returns ~ for any path 8 [1 0] [1 [1 0] 0 30] 0 1]+poke, axis 23: a gate that returns [~ state] (the state is at 30 inside it) 0 1][battery [0 S]], with the state at 6 and S at 7A kernel that does nothing. S is the subject it is built from.
Each arm is itself a gate builder: [8 [1 0] [1 <arm>] 0 1] pushes a default sample of 0 and returns the gate [<arm> 0 <kernel>], so the kernel sits at axis 7 of every gate it builds. Inside the +poke gate the kernel’s state is at axis 30 (axis 6 of the kernel at axis 7), and the arm [[1 0] 0 30] builds the cell [~ state].
A runtime would evaluate this kernel by supplying it with an actual state (sample) at axis +6 (with context/stdlib at +7), then invoking its arms as needed.
Calling an Arm#
Here is a +poke as a runtime would make it, written out in Nock. K is the kernel formula above, the kernel’s state is set to 33, and the cause is 99:
[7 K[battery [0 S]] 7 [10 [6 1 33] 0 1] 8 [9 23 0 1]+poke gate, and push it: the subject is [gate kernel] 9 2 10 [6 1 99] 0 2][0 33]The result is [0 33]: no effects (~), and the state 33, unchanged. A +peek is made the same way with arm 22 and a path as the sample, and returns 0, which is ~.