4title: API overview by prefix
7## u3: API overview by prefix
9Let's run through the `u3` modules one by one. All public
10functions are commented, but the comments may be cryptic.
16 /* u3m_boot(): start the u3 system.
19 u3m_boot(c3_o nuu_o, c3_o bug_o, c3_c* dir_c);
21`nuu_o` is `c3y` (yes, `0`) if you're creating a new pier,
22`c3n` (no, `1`) if you're loading an existing one. `bug_o`
23is `c3y` if you want to test the garbage-collector, `c3n`
24otherwise. `dir_c` is the directory for the pier files.
26`u3m_boot()` expects an `urbit.pill` file to load the kernel
27from. It will try first `$dir/.urb.urbit.pill`, then `U3_LIB`.
29Any significant computation with nouns, certainly anything Turing
30complete, should be run (a) virtualized and (b) in an inner road.
31These are slightly different things, but at the highest level we
32bundle them together for your convenience, in `u3m_soft()`:
34 /* u3m_soft(): system soft wrapper. unifies unix and nock errors.
36 ** Produces [%$ result] or [%error (list tank)].
39 u3m_soft(c3_w sec_w, u3_funk fun_f, u3_noun arg);
41`sec_w` is the number of seconds to time out the computation.
42`fun_f` is a C function accepting `arg`.
44The result of `u3m_soft()` is a cell whose head is an atom. If
45the head is `%$` - ie, `0` - the tail is the result of
46`fun_f(arg)`. Otherwise, the head is a `term` (an atom which is
47an LSB first string), and the tail is a `(list tank)` (a list of
48`tank` printables - see `++tank` in `hoon.hoon`). Error terms
49should be the same as the exception terms above.
51If you're confident that your computation won't fail, you can
52use `u3m_soft_sure()`, `u3m_soft_slam()`, or `u3m_soft_nock()`
53for C functions, Hoon function calls, and Nock invocations.
54Caution - this returns just the result, and asserts globally.
56All the `u3m_soft` functions above work *only on the surface*.
57Within the surface, virtualize with `u3m_soft_run()`. Note that
58this takes a `fly` (a namespace gate), thus activating the `11`
59super-operator in the nock virtualizer, `++mock`. When actually
60using the `fly`, call `u3m_soft_esc()`. Don't do either unless
61you know what you're doing!
63For descending into a subroad *without* Nock virtualization,
64use `u3m_hate()` and `u3m_love` respectively. Hating enters
65a subroad; loving leaves it, copying out a product noun.
67Other miscellaneous tools in `u3m`: `u3m_file()` loads a Unix
68file as a Nock atom; `u3m_water()` measures the boundaries of
69the loom in current use (ie, watermarks); and a variety of
70prettyprinting routines, none perfect, are available, mainly for
71debugging printfs: `u3m_pretty()`, `u3m_p()`, `u3m_tape()` and
74It's sometimes nice to run a mark-and-sweep garbage collector,
75`u3m_grab()`, which collects the world from a list of roots,
76and asserts if it finds any leaks or incorrect refcounts. This
77tool is for debugging and long-term maintenance only; refcounts
82The jet system, `u3j`, is what makes `u3` and `nock` in any sense
83a useful computing environment. Except perhaps `u3a` (there is
84really no such thing as a trivial allocator, though `u3a` is
85dumber than most) - `u3j` is the most interesting code in `u3`.
87Let's consider the minor miracle of driver-to-battery binding
88which lets `u3j` work - and decrement not be `O(n)` - without
89violating the precisely defined semantics of pure Nock, *ever*.
91It's easy to assume that jets represent an architectural coupling
92between Hoon language semantics and Nock interpreter internals.
93Indeed such a coupling would be wholly wrongtious and un-Urbit.
94But the jet system is not Hoon-specific. It is specific to nock
95runtime systems that use a design pattern we call a `core`.
97#### u3j: core structure
99A core is no more than a cell `[code data]`, in which a `code` is
100either a Nock formula or a cell of `code`s, and `data` is anything.
101In a proper core, the subject each formula expects is the core
104Except for the arbitrary decision to make a core `[code data]`,
105(or as we sometimes say, `[battery payload]`), instead of `[data
106code]`, any high-level language transforming itself to Nock would
109So jets are in fact fully general. Broadly speaking, the jet
110system works by matching a C *driver* to a battery. When the
111battery is invoked with Nock operator `9`, it must be found in
112associative memory and linked to its driver. Then we link the
113formula axis of the operation (`a` in `[9 a b]`) to a specific
114function in the driver.
116To validate this jet binding, we need to know two things. One,
117we need to know the C function actually is a perfect semantic
118match for the Nock formula. This can be developed with driver
119test flags, which work, and locked down with a secure formula
120hash in the driver, which we haven't bothered with just yet.
121(You could also try to develop a formal method for verifying
122that C functions and Nock formulas are equivalent, but this is
123a research problem for the future.)
125Two, we need to validate that the payload is appropriate for the
126battery. We should note that jets are a Nock feature and have no
127reference to Hoon. A driver which relies on the Hoon type system
128to only pair it with valid payloads is a broken driver, and
129breaks the Nock compliance of the system as a whole. So don't.
131Now, a casual observer might look at `[battery payload]` and
132expect the simplest case of it to be `[formula subject]`. That
133is: to execute a simple core whose battery is a single formula,
138Then, naturally, when we go from Hoon or a high-level language
139containing functions down to Nock, `[function arguments]` turns
140into `[formula subject]`. This seems like an obvious design, and
141we mention it only because it is *completely wrong*.
143Rather, to execute a one-armed core like the above, we run
147and the normal structure of a `gate`, which is simply Urbitese
150 [formula [sample context]]
152where `sample` is Urbitese for "arguments" - and `context`, any
153Lisper will at once recognize, is Urbitese for "environment."
155To `slam` or call the gate, we simply replace the default sample
156with the caller's data, then nock the formula on the entire gate.
158What's in the context? Unlike in most dynamic languages, it is
159not some secret system-level bag of tricks. Almost always it is
160another core. This onion continues until at the bottom, there is
161an atomic constant, conventionally is the kernel version number.
163Thus a (highly desirable) `static` core is one of the form
166 [battery static-core]
168ie, a solid stack of nested libraries without any dynamic data.
169The typical gate will thus be, for example,
171 [formula [sample [battery battery battery constant]]]
173but we would be most foolish to restrict the jet mechanism to
174cores of this particular structure. We cannot constrain a
175payload to be `[sample static-core]`, or even `[sample core]`.
176Any such constraint would not be rich enough to handle Hoon,
177let alone other languages.
181There are two fundamental rules of computer science: (1) every
182system is best understood through its state; (2) less state is
183better than more state. Sadly, a pier has three different jet
184state systems: `cold`, `warm` and `hot`. It needs all of them.
186Hot state is associated with this particular Unix process. The
187persistent pier is portable not just between process and process,
188but machine and machine or OS and OS. The set of jets loaded
189into a pier may itself change (in theory, though not in the
190present implementation) during the lifetime of the process. Hot
191state is a pure C data structure.
193Cold state is associated with the logical execution history of
194the pier. It consists entirely of nouns and ignores restarts.
196Warm state contains all dependencies between cold and hot
197state. It consists of C structures allocated on the loom.
199Warm state is purely a function of cold and hot states, and
200we can wipe and regenerate it at any time. On any restart where
201the hot state might have changed, we clear the warm state
204There is only one hot state, the global jet dashboard
205`u3j_Dash` or `u3D` for short. In the present implementation,
206u3D is a static structure not modified at runtime, except for
207numbering itself on process initialization. This structure -
208which embeds function pointers to all the jets - is defined
209in `j/tree.c`. The data structures:
211 /* u3j_harm: driver arm.
213 typedef struct _u3j_harm {
214 c3_c* fcs_c; // `.axe` or name
215 u3_noun (*fun_f)(u3_noun); // compute or 0 / semitransfer
216 c3_o ice; // perfect (don't test)
217 c3_o tot; // total (never punts)
218 c3_o liv; // live (enabled)
221 /* u3j_core: C core driver.
223 typedef struct _u3j_core {
224 c3_c* cos_c; // control string
225 struct _u3j_harm* arm_u; // blank-terminated static list
226 struct _u3j_core* dev_u; // blank-terminated static list
227 struct _u3j_core* par_u; // dynamic parent pointer
228 c3_l jax_l; // dynamic jet index
231 /* u3e_dash, u3_Dash, u3D: jet dashboard singleton
233 typedef struct _u3e_dash {
234 u3j_core* dev_u; // null-terminated static list
235 c3_l len_l; // ray_u filled length
236 c3_l all_l; // ray_u allocated length
237 u3j_core* ray_u; // dynamic driver array
240Warm and cold state is *per road*. In other words, as we nest
241roads, we also nest jet state. The jet state in the road is:
243 struct { // jet dashboard
244 u3p(u3h_root) har_p; // warm state
245 u3_noun das; // cold state
248In case you understand Hoon, `das` (cold state) is a `++dash`,
249and `har_p` (warm state) is a map from battery to `++calx`:
251 ++ bane ,@tas :: battery name
252 ++ bash ,@uvH :: label hash
253 ++ bosh ,@uvH :: local battery hash
254 ++ batt ,* :: battery
256 $: jax=,@ud :: hot core index
257 hap=(map ,@ud ,@ud) :: axis/hot arm index
258 lab=path :: label as path
259 jit=* :: arbitrary data
261 ++ calx (trel calf (pair bash cope) club) :: cached by battery
262 ++ clog (pair cope (map batt club)) :: identity record
263 ++ club (pair corp (map term nock)) :: battery pattern
264 ++ cope (trel bane axis (each bash noun)) :: core pattern
266 ++ corp (each core batt) :: parent or static
267 ++ dash (map bash clog) :: jet system
269The driver index `jax` in a `++calx` is an index into `ray_u` in the
270dashboard - ie, a pointer into hot state. This is why the warm
271state has to be reset when we reload the pier in a new process.
273Why is jet state nested? Nock of course is a functional system,
274so as we compute we don't explicitly create state. Jet state is
275an exception to this principle (which works only because it can't
276be semantically detected from Nock/Hoon) - but it can't violate
277the fundamental rules of the allocation system.
279For instance, when we're on an inner road, we can't allocate on
280an outer road, or point from an outer road to an inner. So if we
281learn something - like a mapping from battery to jet - in the
282inner road, we have to keep it in the inner road.
284Mitigating this problem, when we leave an inner road (with
285`u3m_love()`), we call `u3j_reap()` to promote jet information in
286the dying road. Reaping promotes anything we've learned about
287any battery that either (a) already existed in the outer road, or
288(b) is being saved to the outer road.
292Jet binding starts with a `%fast` hint. (In Hoon, this is
293produced by the runes `~%`, for the general case, or `~/`
294for simple functions.) To bind a jet, execute a formula of the
297 [10 [%fast clue-formula] core-formula]
299`core-formula` assembles the core to be jet-propelled.
300`clue-formula` produces the hint information, or `++clue`
301above, which we want to annotate it with.
303A clue is a triple of name, parent, and hooks:
305 ++ clue (trel chum nock (list (pair term nock)))
307The name, or `++chum`, has a bunch of historical structure which
308we don't need (cleaning these things up is tricky), but just gets
309flattened into a term.
311The parent axis is a nock formula, but always reduces to a simple
312axis, which is the address of this core's *parent*. Consider
313again an ordinary gate
315 [formula [sample context]]
317Typically the `context` is itself a library core, which itself
318has a jet binding. If so, the parent axis of this gate is `7`.
320If the parent is already bound - and the parent *must* be already
321bound, in this road or a road containing it - we can hook this core
322bottom-up into a tree hierarchy. Normally the child core is
323produced by an arm of the parent core, so this is not a problem -
324we wouldn't have the child if we hadn't already made the parent.
326The clue also contains a list of *hooks*, named nock formulas on
327the core. Usually these are arms, but they need not be. The
328point is that we often want to call a core from C, in a situation
329where we have no type or other source information. A common case
330of this is a complex system in which we're mixing functions which
331are jet-propelled with functions that aren't.
333In any case, all the information in the `%fast` hint goes to
334`u3j_mine()`, which registers the battery in cold state (`das` in
335`jed` in `u3R`), then warm state (`har_p` in `jed`).
337It's essential to understand that the `%fast` hint has to be,
338well, fast - because we apply it whenever we build a core. For
339instance, if the core is a Hoon gate - a function - we will call
340`u3j_mine` every time the function is called.
342### u3j: the cold jet dashboard
344For even more fun, the jet tree is not actually a tree of
345batteries. It's a tree of battery *labels*, where a label is
346an [axis term] path from the root of the tree. (At the root,
347if the core pattern is always followed properly, is a core whose
348payload is an atomic constant, conventionally the Hoon version.)
350Under each of these labels, it's normal to have an arbitrary
351number of different Nock batteries (not just multiple copies
352of the same noun, a situation we *do* strive to avoid). For
353instance, one might be compiled with debugging hints, one not.
355We might even have changed the semantics of the battery without
356changing the label - so long as those semantics don't invalidate
359For instance, it's normal to have two equivalent Nock batteries
360at the same time in one pier: one battery compiled with
361debugging hints, one not.
363Rather, the jet tree is a semantic hierarchy. The root of the
364hierarchy is a constant, by convention the Hoon kernel version
365because any normal jet-propelled core has, at the bottom of its
366onion of libraries, the standard kernel. Thus if the core is
368 [foo-battery [bar-battery [moo-battery 164]]]
370we can reverse the nesting to construct a hierarchical core
375extends the static core `164/moo/bar` by wrapping the `foo`
376battery (ie, in Hoon, `|%`) around it. With the core above,
377you can compute `foo` stuff, `bar` stuff, and `moo` stuff.
380Not all cores are static, of course - they may contain live data,
381like the sample in a gate (ie, argument to a function). Once
382again, it's important to remember that we track jet bindings not
383by the core, which may not be static, but by the battery, which
386(And if you're wondering how we can use a deep noun like a Nock
387formula or battery as a key in a key-value table, remember
388`mug_w`, the lazily computed short hash, in all boxed nouns.)
390In any case, `das`, the dashboard, is a map from `bash` to jet
391location record (`++clog`). A `clog` in turn contains two kinds
392of information: the `++cope`, or per-location noun; and a map of
393batteries to a per-battery `++club`.
395The `cope` is a triple of `++bane` (battery name, right now just
396a `term`); `++axis`, the axis, within *this* core, of the parent;
397and `(each bash noun)`, which is either `[0 bash]` if the parent
398is another core, or `[1 noun]`, for the constant noun (like
399`164`) if there is no parent core.
401A `bash` is just the noun hash (`++sham`) of a `cope`, which
402uniquely expresses the battery's hierarchical location without
403depending on the actual formulas.
405The `club` contains a `++corp`, which we use to actually validate
406the core. Obviously jet execution has to be perfectly compatible
407with Nock. We search on the battery, but getting the battery
408right is not enough - a typical battery is dependent on its
409context. For example, your jet-propelled library function is
410very likely to call `++dec` or other advanced kernel technology.
411If you've replaced the kernel in your context with something
412else, we need to detect this and not run the jet.
414There are two cases for a jet-propelled core - either the entire
415core is a static constant, or it isn't. Hence the definition
418 ++ corp (each core batt) :: parent or static
420Ie, a `corp` is `[0 core]` or `[1 batt]`. If it's static -
421meaning that the jet only works with one specific core, ie, the
422parent axis of each location in the hierarchy is `3` - we can
423validate with a single comparison. Otherwise, we have to recurse
424upward by checking the parent.
426Note that there is at present no way to force a jet to depend on
429### u3j: the warm jet dashboard
431We don't use the cold state to match jets as we call them. We
432use the cold state to register jets as we find them, and also to
433rebuild the warm state after the hot state is reset.
435What we actually use at runtime is the warm state, `jed->har_p`,
436which is a `u3h` (built-in hashtable), allocated on the loom,
437from battery to `++calx`.
439A `calx` is a triple of a `++calf`, a `[bash cope]` cell, and a
440`club`. The latter two are all straight from cold state.
442The `calf` contains warm data dependent on hot state. It's a
443quadruple: of `jax`, the hot driver index (in `ray_u` in
444`u3j_dash`); `hap`, a table from arm axis (ie, the axis of each
445formula within the battery) to driver arm index (into `arm_u` in
446`u3j_core`); `lab`, the complete label path; and `jit`, any
447other dynamic data that may speed up execution.
449We construct `hap`, when we create the calx, by iterating through
450the arms registered in the `u3j_core`. Note the way a `u3j_harm`
451declares itself, with the string `fcs_c` which can contain either
452an axis or a name. Most jetted cores are of course gates, which
453have one formula at one axis within the core: `fcs_c` is `".3"`.
455But we do often have fast cores with more complex arm structure,
456and it would be sad to have to manage their axes by hand. To use
457an `fcs_c` with a named arm, it's sufficient to make sure the
458name is bound to a formula `[0 axis]` in the hook table.
460`jit`, as its name suggests, is a stub where any sort of
461optimization data computed on battery registration might go. To
462use it, fill in the `_cj_jit()` function.
464### u3j: the hot dashboard
466Now it should be easy to see how we actually invoke jets. Every
467time we run a nock `9` instruction (pretty often, obviously), we
468have a core and an axis. We pass these to `u3j_kick()`, which
469will try to execute them.
471Because nouns with a reference count of 1 are precious,
472`u3j_kick()` has a tricky reference control definition. It
473reserves the right to return `u3_none` in the case where there is
474no driver, or the driver does not apply for this case; in this
475case, it retains argument `cor`. If it succeeds, though, it
478`u3j_kick()` searches for the battery (always the head of the
479core, of course) in the hot dashboard. If the battery is
480registered, it searches for the axis in `hap` in the `calx`.
481If it exists, the core matches a driver and the driver jets this
482arm. If not, we return `u3_none`.
484Otherwise, we call `fun_f` in our `u3j_harm`. This obeys the
485same protocol as `u3j_kick()`; it can refuse to function by
486returning `u3_none`, or consume the noun.
488Besides the actual function pointer `fun_f`, we have some flags
489in the `u3j_harm` which tell us how to call the arm function.
491If `ice` is yes (`&`, `0`), the jet is known to be perfect and we
492can just trust the product of `fun_f`. Otherwise, we need to run
493*both* the Nock arm and `fun_f`, and compare their results.
495(Note that while executing the C side of this test, we have to
496set `ice` to yes; on the Nock side, we have to set `liv` to no.
497Otherwise, many non-exponential functions become exponential.
498When auto-testing jets in this way, the principle is that the
499test is on the outermost layer of recursion.)
501(Note also that anyone who multi-threads this execution
502environment has a slight locking problem with these flags if arm
503testing is multi-threaded.)
505If `tot` is yes, (`&`, `0`), the arm function is *total* and has
506to return properly (though it can still return *u3_none*).
507Otherwise, it is *partial* and can `u3_cm_bail()` out with
508c3__punt. This feature has a cost: the jet runs in a subroad.
510Finally, if `liv` is no (`|`, 1), the jet is off and doesn't run.
512It should be easy to see how the tree of cores gets declared -
513precisely, in `j/dash.c`. We declare the hierarchy as a tree
514of `u3j_core` structures, each of which comes with a static list
515of arms `arm_u` and sub-cores `dev_u`.
517In `u3j_boot()`, we traverse the hierarchy, fill in parent
518pointers `par_u`, and enumerate all `u3j_core` structures
519into a single flat array `u3j_dash.ray_u`. Our hot state
520then appears ready for action.
522### u3j: jet functions
524At present, all drivers are compiled statically into `u3`. This is
525not a long-term permanent solution or anything. However, it will
526always be the case with a certain amount of core functionality.
528For instance, there are some jet functions that we need to call
529as part of loading the Arvo kernel - like `++cue` to unpack a
530noun from an atom. And obviously it makes sense, when jets are
531significant enough to compile into `u3`, to export their symbols
532in headers and the linker.
534There are three interface prefixes for standard jet functions:
535`u3k`, `u3q`, and `u3w`. All jets have `u3w` interfaces; most
536have `u3q`; some have `u3k`. Of course the actual logic is
539`u3w` interfaces use the same protocol as `fun_f` above: the
540caller passes the entire core, which is retained if the function
541returns `u3_none`, transferred otherwise. Why? Again, use
542counts of 1 are special and precious for performance hackers.
544`u3q` interfaces break the core into C arguments, *retain* noun
545arguments, and *transfer* noun returns. `u3k` interfaces are the
546same, except with more use of `u3_none` and other simple C
547variations on the Hoon original, but *transfer* both arguments
548and returns. Generally, `u3k` are most convenient for new code.
550Following `u3k/q/w` is `[a-f]`, corresponding to the 6 logical
551tiers of the kernel, or `g` for user-level jets. Another letter
552is added for functions within subcores. The filename, under
553`j/`, follows the tier and the function name.
555For instance, `++add` is `u3wa_add(cor)`, `u3qa_add(a, b)`, or
556`u3ka_add(a, b)`, in `j/a/add.c`. `++get` in `++by` is
557`u3wdb_get(cor)`, `u3kdb_get(a, b)`, etc, in `j/d/by_get.c`.
559For historical reasons, all internal jet code in `j/[a-f]`
560*retains* noun arguments, and *transfers* noun results. Please
561do not do this in new `g` jets! The new standard protocol is to
562transfer both arguments and results.
564### u3a: allocation functions
566`u3a` allocates on the current road (u3R). Its internal
567structures are uninteresting and typical of a naive allocator.
569The two most-used `u3a` functions are `u3a_gain()` to add a
570reference count, and `u3a_lose()` to release one (and free the
571noun, if the use count is zero). For convenience, `u3a_gain()`
572returns its argument. The pair are generally abbreviated with
573the macros `u3k()` and `u3z()` respectively.
575Normally we create nouns through `u3i` functions, and don't call
576the `u3a` allocators directly. But if you do:
578One, there are *two* sets of allocators: the word-aligned
579allocators and the fully-aligned (ie, malloc compatible)
580allocators. For instance, on a typical OS X setup, malloc
581produces 16-byte aligned results - needed for some SSE
584These allocators are *not compatible*. For 32-bit alignment
585as used in nouns, call
587 /* u3a_walloc(): allocate storage measured in words.
590 u3a_walloc(c3_w len_w);
592 /* u3a_wfree(): free storage.
595 u3a_wfree(void* lag_v);
597 /* u3a_wealloc(): word realloc.
600 u3a_wealloc(void* lag_v, c3_w len_w);
602For full alignment, call:
604 /* u3a_malloc(): aligned storage measured in bytes.
607 u3a_malloc(size_t len_i);
609 /* u3a_realloc(): aligned realloc in bytes.
612 u3a_realloc(void* lag_v, size_t len_i);
614 /* u3a_realloc2(): gmp-shaped realloc.
617 u3a_realloc2(void* lag_v, size_t old_i, size_t new_i);
619 /* u3a_free(): free for aligned malloc.
622 u3a_free(void* tox_v);
624 /* u3a_free2(): gmp-shaped free.
627 u3a_free2(void* tox_v, size_t siz_i);
629There are also a set of special-purpose allocators for building
630atoms. When building atoms, please remember that it's incorrect
631to have a high 0 word - the word length in the atom structure
632must be strictly correct.
634Of course, we don't always know how large our atom will be.
635Therefore, the standard way of building large atoms is to
636allocate a block of raw space with `u3a_slab()`, then chop off
637the end with `u3a_malt()` (which does the measuring itself)
638or `u3a_mint()` in case you've measured it yourself.
640Once again, *do not call `malloc()`* (or C++ `new`) within any
641code that may be run within a jet. This will cause rare sporadic
642corruption when we interrupt execution within a `malloc()`. We'd
643just override the symbol, but `libuv` uses `malloc()` across
644threads within its own synchronization primitives - for this to
645work with `u3a_malloc()`, we'd have to introduce our own locks on
646the surface-level road (which might be a viable solution).
648### u3n: nock execution
650The `u3n` routines execute Nock itself. On the inside, they have
651a surprising resemblance to the spec proper (the only interesting
652detail is how we handle tail-call elimination) and are, as one
653would expect, quite slow. (There is no such thing as a fast tree
656There is only one Nock, but there are lots of ways to call it.
657(Remember that all `u3n` functions *transfer* C arguments and
660The simplest interpreter, `u3n_nock_on(u3_noun bus, u3_noun fol)`
661invokes Nock on `bus` (the subject) and `fol` (the formula).
662(Why is it`[subject formula]`, not `[formula subject]`? The same
663reason `0` is true and `1` is false.)
665A close relative is `u3n_slam_on(u3_noun gat, u3_noun sam)`,
666which slams a *gate* (`gat`) on a sample (`sam`). (In a normal
667programming language which didn't talk funny and was retarded,
668`u3n_slam_on()` would call a function on an argument.) We could
669write it most simply as:
672 u3n_slam_on(u3_noun gat, u3_noun sam)
674 u3_noun pro = u3n_nock_on
676 u3nc(sam, u3k(u3t(u3t(gat))))),
682Simpler is `u3n_kick_on(u3_noun gat)`, which slams a gate (or,
683more generally, a *trap* - because sample structure is not even
684needed here) without changing its sample:
687 u3n_kick_on(u3_noun gat, u3_noun sam)
689 return u3n_nock_on(gat, u3k(u3h(gat)));
692The `_on` functions in `u3n` are all defined as pure Nock. But
693actually, even though we say we don't extend Nock, we do. But we
696Note that `u3` has a well-developed error handling system -
697`u3m_bail()` to throw an exception, `u3m_soft_*` to catch one.
698But Nock has no exception model at all. That's okay - all it
699means if that if an `_on` function bails, the exception is an
700exception in the caller.
702However, `u3`'s exception handling happens to match a convenient
703virtual super-Nock in `hoon.hoon`, the infamous `++mock`. Of
704course, Nock is slow, and `mock` is Nock in Nock, so it is
705(logically) super-slow. Then again, so is decrement.
707With the power of `u3`, we nest arbitrary layers of `mock`
708without any particular performance cost. Moreover, we simply
709treat Nock proper as a special case of `mock`. (More precisely,
710the internal VM loop is `++mink` and the error compiler is
711`++mook`. But we call the whole sandbox system `mock`.)
713The nice thing about `mock` functions is that (by executing
714within `u3m_soft_run()`, which as you may recall uses a nested
715road) they provide both exceptions and the namespace operator -
716`.^` in Hoon, which becomes operator `11` in `mock`.
718`11` requires a namespace function, or `fly`, which produces a
719`++unit` - `~` (`0`) for no binding, or `[0 value]`. The sample
720to a `fly` is a `++path`, just a list of text `span`.
722`mock` functions produce a `++toon`. Fully elaborated:
724 ++ noun ,* :: any noun
725 ++ path (list ,@ta) :: namespace path
726 ++ span ,@ta :: text-atom (ASCII)
727 ++ toon $% [%0 p=noun] :: success
728 [%1 p=(list path)] :: blocking paths
729 [%2 p=(list tank)] :: stack trace
732 $% [%leaf p=tape] :: flat text
733 $: %palm :: backstep list
734 p=[p=tape q=tape r=tape s=tape] :: mid cap open close
735 q=(list tank) :: contents
737 $: %rose :: straight list
738 p=[p=tape q=tape r=tape] :: mid open close
739 q=(list tank) :: contents
743(Note that `tank` is overdesigned and due for replacement.)
745What does a `toon` mean? Either your computation succeded (`[0
746noun]`, or could not finish because it blocked on one or more
747global paths (`[1 (list path)]`), or it exited with a stack trace
750Note that of all the `u3` exceptions, only `%exit` is produced
751deterministically by the Nock definition. Therefore, only
752`%exit` produces a `2` result. Any other argument to
753`u3m_bail()` will unwind the virtualization stack all the way to
754the top - or to be more exact, to `u3m_soft_top()`.
756In any case, the simplest `mock` functions are `u3n_nock_un()`
757and `u3n_slam_un()`. These provide exception control without
758any namespace change, as you can see by the code:
760 /* u3n_nock_un(): produce .*(bus fol), as ++toon.
763 u3n_nock_un(u3_noun bus, u3_noun fol)
765 u3_noun fly = u3nt(u3nt(11, 0, 6), 0, 0); // |=(a=* .^(a))
767 return u3n_nock_in(fly, bus, fol);
770 /* u3n_slam_un(): produce (gat sam), as ++toon.
773 u3n_slam_un(u3_noun gat, u3_noun sam)
775 u3_noun fly = u3nt(u3nt(11, 0, 6), 0, 0); // |=(a=* .^(a))
777 return u3n_slam_in(fly, gat, sam);
780The `fly` is added as the first argument to `u3n_nock_in()` and
781`u3n_slam_in()`. Of course, logically, `fly` executes in the
782caller's exception layer. (Maintaining this illusion is slightly
783nontrivial.) Finally, `u3n_nock_an()` is a sandbox with a null
788The only `u3e` function you should need to call is `u3e_save()`,
789which saves the loom. As it can be restored on any platform,
790please make sure you don't have any state in the loom that is
791bound to your process or architecture - except for exceptions
792like the warm jet state, which is actively purged on reboot.
794### u3r: reading nouns (weak)
796As befits accessors they don't make anything, `u3r` noun reading
797functions always retain their arguments and their returns. They
798never bail; rather, when they don't work, they return a `u3_weak`
801Most of these functions are straightforward and do only what
802their comments say. A few are interesting enough to discuss.
804`u3r_at()` is the familiar tree fragment function, `/` from the
805Nock spec. For taking complex nouns apart, `u3r_mean()` is a
806relatively funky way of deconstructing nouns with a varargs list
807of `axis`, `u3_noun *`. For cells, triples, etc, decompose with
808`u3r_cell()`, `u3r_trel()`, etc. For the tagged equivalents, use
809`u3r_pq()` and friends.
811`u3r_sing(u3_noun a, u3_noun b)` (true if `a` and `b` are a
812*single* noun) are interesting because it uses mugs to help it
813out. Clearly, different nouns may have the same mug, but the
814same nouns cannot have a different mug. It's important to
815understand the performance characteristics of `u3r_sing()`:
816the worst possible case is a comparison of duplicate nouns,
817which have the same value but were created separately. In this
818case, the tree is traversed
820`u3r_sung()` is a deeply funky and frightening version of
821`u3r_sing()` that unifies pointers to the duplicate nouns it
822finds, freeing the second copy. Obviously, do not use
823`u3r_sung()` when you have live, but not reference counted, noun
824references from C - if they match a noun with a refcount of 1
825that gets freed, bad things happen.
827It's important to remember that `u3r_mug()`, which produces a
82831-bit, nonzero insecure hash, uses the `mug_w` slot in any boxed
829noun as a lazy cache. There are a number of variants of
830`u3r_mug()` that can get you out of building unneeded nouns.
832### u3x: reading nouns (bail)
834`u3x` functions are like `u3r` functions, but instead of
835returning `u3_none` when (for instance) we try to take the head
836of an atom, they bail with `%exit`. In other words, they do what
837the same operation would do in Nock.
841We can of course use the Hoon `map` structure as an associative
842array. This is a balanced treap and reasonably fast. However,
843it's considerably inferior to a custom structure like an HAMT
844(hash array-mapped trie). We use `u3_post` to allocate HAMT
845structures on the loom.
847(Our HAMT implements the classic Bagwell algorithm which depends
848on the `gcc` standard directive `__builtin_popcount()`. On a CPU
849which doesn't support popcount or an equivalent instruction, some
850other design would probably be preferable.)
852There's no particular rocket science in the API. `u3h_new()`
853creates a hashtable; `u3h_free()` destroys one; `u3h_put()`
854inserts, `u3h_get()` retrieves. You can transform values in a
855hashtable with `u3h_walk()`.
857The only funky function is `u3h_gut()`, which unifies keys with
858`u3r_sung()`. As with all cases of `u3r_sung()`, this must be
859used with extreme caution.
863Connected to the `~+` rune in Hoon, via the Nock `%memo` hint,
864the memoization facility is a general-purpose cache.
866(It's also used for partial memoization - a feature that'll
867probably be removed, in which conservative worklist algorithms
868(which would otherwise be exponential) memoize everything in the
869subject *except* the worklist. This is used heavily in the Hoon
870compiler jets (j/f/*.c). Unfortunately, it's probably not
871possible to make this work perfectly in that it can't be abused
872to violate Nock, so we'll probably remove it at a later date,
873instead making `++ut` keep its own monadic cache.)
875Each `u3z` function comes with a `c3_m` mote which disambiguates
876the function mapping key to value. For Nock itself, use 0. For
877extra speed, small tuples are split out in C; thus, find with
879 u3_weak u3z_find(c3_m, u3_noun);
880 u3_weak u3z_find_2(c3_m, u3_noun, u3_noun);
881 u3_weak u3z_find_3(c3_m, u3_noun, u3_noun, u3_noun);
882 u3_weak u3z_find_4(c3_m, u3_noun, u3_noun, u3_noun, u3_noun);
886 u3_noun u3z_save(c3_m, u3_noun, u3_noun);
887 u3_noun u3z_save_2(c3_m, u3_noun, u3_noun, u3_noun);
888 u3_noun u3z_save_3(c3_m, u3_noun, u3_noun, u3_noun, u3_noun);
889 u3_noun u3z_save_4(c3_m, u3_noun, u3_noun, u3_noun, u3_noun, u3_noun);
891where the value is the last argument. To eliminate duplicate
897`u3z` functions retain keys and transfer values.
899The `u3z` cache, built on `u3h` hashes, is part of the current
900road, and goes away when it goes away. (In future, we may wish
901to promote keys/values which outlive the road, as we do with jet
902state.) There is no cache reclamation at present, so be careful.
904### u3t: tracing and profiling.
908### u3v: the Arvo kernel
910An Arvo kernel - or at least, a core that compiles with the Arvo
911interface - is part of the global `u3` state. What is an Arvo
912core? Slightly pseudocoded:
916 ++ come |/ {yen/@ ova/(list ovum) nyf/pone} :: 11
919 ++ keep |/ {now/@da hap/path} :: 4
922 ++ load |/ {yen/@ ova/(list ovum) nyf/pane} :: 86
925 ++ peek |/ {now/@da path} :: 87
928 ++ poke |/ {now/@da ovo/ovum} :: 42
931 ++ wish |/ txt/@ta :: 20
935 ++ card {p/@tas q/*} :: typeless card
936 ++ ovum {p/wire q/card} :: Arvo event
937 ++ wire path :: event cause
939This is the Arvo ABI in a very real sense. Arvo is a core with
940these six arms. To use these arms, we hardcode the axis of the
941formula (`11`, `4`, `86`, etc) into the C code that calls Arvo,
942because otherwise we'd need type metadata - which we can get, by
945It's important to understand the Arvo event/action structure, or
946`++ovum`. An `ovum` is a `card`, which is any `[term noun]`
947cell, and a `++wire`, a `path` which indicates the location of
948the event. At the Unix level, the `wire` corresponds to a system
949module or context. For input events, this is the module that
950caused the event; for output actions, it's the module that
953`++poke` sends Arvo an event `ovum`, producing a cell of action
954ova and a new Arvo core.
956`++peek` dereferences the Arvo namespace. It takes a date and a
957key, and produces `~` (`0`) or `[~ value]`.
959`++keep` asks Arvo the next time it wants to be woken up, for the
960given `wire`. (This input will probably be eliminated in favor
961of a single global timer.)
963`++wish` compiles a string of Hoon source. While just a
964convenience, it's a very convenient convenience.
966`++come` and `++load` are used by Arvo to reset itself (more
967precisely, to shift the Arvo state from an old kernel to a new
968one); there is no need to call them from C.
970Now that we understand the Arvo kernel interface, let's look at
971the `u3v` API. As usual, all the functions in `u3v` are
972commented, but unfortunately it's hard to describe this API as
973clean at present. The problem is that `u3v` remains design
974coupled to the old `vere` event handling code written for `u2`.
975But let's describe the functions you should be calling, assuming
976you're not writing the next event system. There are only two.
978`u3v_wish(str_c)` wraps the `++wish` functionality in a cache
979(which is read-only unless you're on the surface road).
981`u3v_do()` uses `wish` to provide a convenient interface for
982calling Hoon kernel functions by name. Even more conveniently,
983we tend to call `u3v_do()` with these convenient aliases:
985 #define u3do(txt_c, arg) u3v_do(txt_c, arg)
986 #define u3dc(txt_c, a, b) u3v_do(txt_c, u3nc(a, b))
987 #define u3dt(txt_c, a, b, c) u3v_do(txt_c, u3nt(a, b, c))
988 #define u3dq(txt_c, a, b, c, d) u3v_do(txt_c, u3nt(a, b, c, d))
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list