2title = "API overview by prefix"
5aliases = ["/docs/learn/vere/api"]
7Let's run through the `u3` modules one by one. All public
8functions are commented, but the comments may be cryptic.
15/* u3m_boot(): start the u3 system.
18 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()`:
35/* u3m_soft(): system soft wrapper. unifies unix and nock errors.
37** Produces [%$ result] or [%error (list tank)].
40 u3m_soft(c3_w sec_w, u3_funk fun_f, u3_noun arg);
43`sec_w` is the number of seconds to time out the computation.
44`fun_f` is a C function accepting `arg`.
46The result of `u3m_soft()` is a cell whose head is an atom. If
47the head is `%$` - ie, `0` - the tail is the result of
48`fun_f(arg)`. Otherwise, the head is a `term` (an atom which is
49an LSB first string), and the tail is a `(list tank)` (a list of
50`tank` printables - see `++tank` in `hoon.hoon`). Error terms
51should be the same as the exception terms above.
53If you're confident that your computation won't fail, you can
54use `u3m_soft_sure()`, `u3m_soft_slam()`, or `u3m_soft_nock()`
55for C functions, Hoon function calls, and Nock invocations.
56Caution - this returns just the result, and asserts globally.
58All the `u3m_soft` functions above work **only on the surface**.
59Within the surface, virtualize with `u3m_soft_run()`. Note that
60this takes a `fly` (a namespace gate), thus activating the `11`
61super-operator in the nock virtualizer, `++mock`. When actually
62using the `fly`, call `u3m_soft_esc()`. Don't do either unless
63you know what you're doing!
65For descending into a subroad **without** Nock virtualization,
66use `u3m_hate()` and `u3m_love` respectively. Hating enters
67a subroad; loving leaves it, copying out a product noun.
69Other miscellaneous tools in `u3m`: `u3m_file()` loads a Unix
70file as a Nock atom; `u3m_water()` measures the boundaries of
71the loom in current use (ie, watermarks); and a variety of
72prettyprinting routines, none perfect, are available, mainly for
73debugging printfs: `u3m_pretty()`, `u3m_p()`, `u3m_tape()` and
76It's sometimes nice to run a mark-and-sweep garbage collector,
77`u3m_grab()`, which collects the world from a list of roots,
78and asserts if it finds any leaks or incorrect refcounts. This
79tool is for debugging and long-term maintenance only; refcounts
84The jet system, `u3j`, is what makes `u3` and `nock` in any sense
85a useful computing environment. Except perhaps `u3a` (there is
86really no such thing as a trivial allocator, though `u3a` is
87dumber than most) - `u3j` is the most interesting code in `u3`.
89Let's consider the minor miracle of driver-to-battery binding
90which lets `u3j` work - and decrement not be `O(n)` - without
91violating the precisely defined semantics of pure Nock, **ever**.
93It's easy to assume that jets represent an architectural coupling
94between Hoon language semantics and Nock interpreter internals.
95Indeed such a coupling would be wholly wrongtious and un-Urbit.
96But the jet system is not Hoon-specific. It is specific to nock
97runtime systems that use a design pattern we call a `core`.
99#### u3j: core structure
101A core is no more than a cell `[code data]`, in which a `code` is
102either a Nock formula or a cell of `code`s, and `data` is anything.
103In a proper core, the subject each formula expects is the core
106Except for the arbitrary decision to make a core `[code data]`,
107(or as we sometimes say, `[battery payload]`), instead of `[data
108code]`, any high-level language transforming itself to Nock would
111So jets are in fact fully general. Broadly speaking, the jet
112system works by matching a C **driver** to a battery. When the
113battery is invoked with Nock operator `9`, it must be found in
114associative memory and linked to its driver. Then we link the
115formula axis of the operation (`a` in `[9 a b]`) to a specific
116function in the driver.
118To validate this jet binding, we need to know two things. One,
119we need to know the C function actually is a perfect semantic
120match for the Nock formula. This can be developed with driver
121test flags, which work, and locked down with a secure formula
122hash in the driver, which we haven't bothered with just yet.
123(You could also try to develop a formal method for verifying
124that C functions and Nock formulas are equivalent, but this is
125a research problem for the future.)
127Two, we need to validate that the payload is appropriate for the
128battery. We should note that jets are a Nock feature and have no
129reference to Hoon. A driver which relies on the Hoon type system
130to only pair it with valid payloads is a broken driver, and
131breaks the Nock compliance of the system as a whole. So don't.
133Now, a casual observer might look at `[battery payload]` and
134expect the simplest case of it to be `[formula subject]`. That
135is: to execute a simple core whose battery is a single formula,
142Then, naturally, when we go from Hoon or a high-level language
143containing functions down to Nock, `[function arguments]` turns
144into `[formula subject]`. This seems like an obvious design, and
145we mention it only because it is **completely wrong**.
147Rather, to execute a one-armed core like the above, we run
153and the normal structure of a `gate`, which is simply Urbitese
157[formula [sample context]]
160where `sample` is Urbitese for "arguments" - and `context`, any
161Lisper will at once recognize, is Urbitese for "environment."
163To `slam` or call the gate, we simply replace the default sample
164with the caller's data, then nock the formula on the entire gate.
166What's in the context? Unlike in most dynamic languages, it is
167not some secret system-level bag of tricks. Almost always it is
168another core. This onion continues until at the bottom, there is
169an atomic constant, conventionally is the kernel version number.
171Thus a (highly desirable) `static` core is one of the form
178ie, a solid stack of nested libraries without any dynamic data.
179The typical gate will thus be, for example,
182[formula [sample [battery battery battery constant]]]
185but we would be most foolish to restrict the jet mechanism to
186cores of this particular structure. We cannot constrain a
187payload to be `[sample static-core]`, or even `[sample core]`.
188Any such constraint would not be rich enough to handle Hoon,
189let alone other languages.
193There are two fundamental rules of computer science: (1) every
194system is best understood through its state; (2) less state is
195better than more state. Sadly, a pier has three different jet
196state systems: `cold`, `warm` and `hot`. It needs all of them.
198Hot state is associated with this particular Unix process. The
199persistent pier is portable not just between process and process,
200but machine and machine or OS and OS. The set of jets loaded
201into a pier may itself change (in theory, though not in the
202present implementation) during the lifetime of the process. Hot
203state is a pure C data structure.
205Cold state is associated with the logical execution history of
206the pier. It consists entirely of nouns and ignores restarts.
208Warm state contains all dependencies between cold and hot
209state. It consists of C structures allocated on the loom.
211Warm state is purely a function of cold and hot states, and
212we can wipe and regenerate it at any time. On any restart where
213the hot state might have changed, we clear the warm state
216There is only one hot state, the global jet dashboard
217`u3j_Dash` or `u3D` for short. In the present implementation,
218u3D is a static structure not modified at runtime, except for
219numbering itself on process initialization. This structure -
220which embeds function pointers to all the jets - is defined
221in `j/tree.c`. The data structures:
224 /* u3j_harm: driver arm.
226 typedef struct _u3j_harm {
227 c3_c* fcs_c; // `.axe` or name
228 u3_noun (*fun_f)(u3_noun); // compute or 0 / semitransfer
229 c3_o ice; // perfect (don't test)
230 c3_o tot; // total (never punts)
231 c3_o liv; // live (enabled)
234 /* u3j_core: C core driver.
236 typedef struct _u3j_core {
237 c3_c* cos_c; // control string
238 struct _u3j_harm* arm_u; // blank-terminated static list
239 struct _u3j_core* dev_u; // blank-terminated static list
240 struct _u3j_core* par_u; // dynamic parent pointer
241 c3_l jax_l; // dynamic jet index
244 /* u3e_dash, u3_Dash, u3D: jet dashboard singleton
246 typedef struct _u3e_dash {
247 u3j_core* dev_u; // null-terminated static list
248 c3_l len_l; // ray_u filled length
249 c3_l all_l; // ray_u allocated length
250 u3j_core* ray_u; // dynamic driver array
254Warm and cold state is **per road**. In other words, as we nest
255roads, we also nest jet state. The jet state in the road is:
258 struct { // jet dashboard
259 u3p(u3h_root) har_p; // warm state
260 u3_noun das; // cold state
264In case you understand Hoon, `das` (cold state) is a `++dash`,
265and `har_p` (warm state) is a map from battery to `++calx`:
268 ++ bane ,@tas :: battery name
269 ++ bash ,@uvH :: label hash
270 ++ bosh ,@uvH :: local battery hash
271 ++ batt ,* :: battery
273 $: jax=,@ud :: hot core index
274 hap=(map ,@ud ,@ud) :: axis/hot arm index
275 lab=path :: label as path
276 jit=* :: arbitrary data
278 ++ calx (trel calf (pair bash cope) club) :: cached by battery
279 ++ clog (pair cope (map batt club)) :: identity record
280 ++ club (pair corp (map term nock)) :: battery pattern
281 ++ cope (trel bane axis (each bash noun)) :: core pattern
283 ++ corp (each core batt) :: parent or static
284 ++ dash (map bash clog) :: jet system
287The driver index `jax` in a `++calx` is an index into `ray_u` in the
288dashboard - ie, a pointer into hot state. This is why the warm
289state has to be reset when we reload the pier in a new process.
291Why is jet state nested? Nock of course is a functional system,
292so as we compute we don't explicitly create state. Jet state is
293an exception to this principle (which works only because it can't
294be semantically detected from Nock/Hoon) - but it can't violate
295the fundamental rules of the allocation system.
297For instance, when we're on an inner road, we can't allocate on
298an outer road, or point from an outer road to an inner. So if we
299learn something - like a mapping from battery to jet - in the
300inner road, we have to keep it in the inner road.
302Mitigating this problem, when we leave an inner road (with
303`u3m_love()`), we call `u3j_reap()` to promote jet information in
304the dying road. Reaping promotes anything we've learned about
305any battery that either (a) already existed in the outer road, or
306(b) is being saved to the outer road.
310Jet binding starts with a `%fast` hint. (In Hoon, this is
311produced by the runes `~%`, for the general case, or `~/`
312for simple functions.) To bind a jet, execute a formula of the
316[10 [%fast clue-formula] core-formula]
319`core-formula` assembles the core to be jet-propelled.
320`clue-formula` produces the hint information, or `++clue`
321above, which we want to annotate it with.
323A clue is a triple of name, parent, and hooks:
326++ clue (trel chum nock (list (pair term nock)))
329The name, or `++chum`, has a bunch of historical structure which
330we don't need (cleaning these things up is tricky), but just gets
331flattened into a term.
333The parent axis is a nock formula, but always reduces to a simple
334axis, which is the address of this core's **parent**. Consider
335again an ordinary gate
338[formula [sample context]]
341Typically the `context` is itself a library core, which itself
342has a jet binding. If so, the parent axis of this gate is `7`.
344If the parent is already bound - and the parent **must** be already
345bound, in this road or a road containing it - we can hook this core
346bottom-up into a tree hierarchy. Normally the child core is
347produced by an arm of the parent core, so this is not a problem -
348we wouldn't have the child if we hadn't already made the parent.
350The clue also contains a list of **hooks**, named nock formulas on
351the core. Usually these are arms, but they need not be. The
352point is that we often want to call a core from C, in a situation
353where we have no type or other source information. A common case
354of this is a complex system in which we're mixing functions which
355are jet-propelled with functions that aren't.
357In any case, all the information in the `%fast` hint goes to
358`u3j_mine()`, which registers the battery in cold state (`das` in
359`jed` in `u3R`), then warm state (`har_p` in `jed`).
361It's essential to understand that the `%fast` hint has to be,
362well, fast - because we apply it whenever we build a core. For
363instance, if the core is a Hoon gate - a function - we will call
364`u3j_mine` every time the function is called.
366### u3j: the cold jet dashboard
368For even more fun, the jet tree is not actually a tree of
369batteries. It's a tree of battery **labels**, where a label is
370an [axis term] path from the root of the tree. (At the root,
371if the core pattern is always followed properly, is a core whose
372payload is an atomic constant, conventionally the Hoon version.)
374Under each of these labels, it's normal to have an arbitrary
375number of different Nock batteries (not just multiple copies
376of the same noun, a situation we **do** strive to avoid). For
377instance, one might be compiled with debugging hints, one not.
379We might even have changed the semantics of the battery without
380changing the label - so long as those semantics don't invalidate
383For instance, it's normal to have two equivalent Nock batteries
384at the same time in one pier: one battery compiled with
385debugging hints, one not.
387Rather, the jet tree is a semantic hierarchy. The root of the
388hierarchy is a constant, by convention the Hoon kernel version
389because any normal jet-propelled core has, at the bottom of its
390onion of libraries, the standard kernel. Thus if the core is
393[foo-battery [bar-battery [moo-battery 164]]]
396we can reverse the nesting to construct a hierarchical core
403extends the static core `164/moo/bar` by wrapping the `foo`
404battery (ie, in Hoon, `|%`) around it. With the core above,
405you can compute `foo` stuff, `bar` stuff, and `moo` stuff.
408Not all cores are static, of course - they may contain live data,
409like the sample in a gate (ie, argument to a function). Once
410again, it's important to remember that we track jet bindings not
411by the core, which may not be static, but by the battery, which
414(And if you're wondering how we can use a deep noun like a Nock
415formula or battery as a key in a key-value table, remember
416`mug_w`, the lazily computed short hash, in all boxed nouns.)
418In any case, `das`, the dashboard, is a map from `bash` to jet
419location record (`++clog`). A `clog` in turn contains two kinds
420of information: the `++cope`, or per-location noun; and a map of
421batteries to a per-battery `++club`.
423The `cope` is a triple of `++bane` (battery name, right now just
424a `term`); `++axis`, the axis, within **this** core, of the parent;
425and `(each bash noun)`, which is either `[0 bash]` if the parent
426is another core, or `[1 noun]`, for the constant noun (like
427`164`) if there is no parent core.
429A `bash` is just the noun hash (`++sham`) of a `cope`, which
430uniquely expresses the battery's hierarchical location without
431depending on the actual formulas.
433The `club` contains a `++corp`, which we use to actually validate
434the core. Obviously jet execution has to be perfectly compatible
435with Nock. We search on the battery, but getting the battery
436right is not enough - a typical battery is dependent on its
437context. For example, your jet-propelled library function is
438very likely to call `++dec` or other advanced kernel technology.
439If you've replaced the kernel in your context with something
440else, we need to detect this and not run the jet.
442There are two cases for a jet-propelled core - either the entire
443core is a static constant, or it isn't. Hence the definition
447++ corp (each core batt) :: parent or static
450Ie, a `corp` is `[0 core]` or `[1 batt]`. If it's static -
451meaning that the jet only works with one specific core, ie, the
452parent axis of each location in the hierarchy is `3` - we can
453validate with a single comparison. Otherwise, we have to recurse
454upward by checking the parent.
456Note that there is at present no way to force a jet to depend on
459### u3j: the warm jet dashboard
461We don't use the cold state to match jets as we call them. We
462use the cold state to register jets as we find them, and also to
463rebuild the warm state after the hot state is reset.
465What we actually use at runtime is the warm state, `jed->har_p`,
466which is a `u3h` (built-in hashtable), allocated on the loom,
467from battery to `++calx`.
469A `calx` is a triple of a `++calf`, a `[bash cope]` cell, and a
470`club`. The latter two are all straight from cold state.
472The `calf` contains warm data dependent on hot state. It's a
473quadruple: of `jax`, the hot driver index (in `ray_u` in
474`u3j_dash`); `hap`, a table from arm axis (ie, the axis of each
475formula within the battery) to driver arm index (into `arm_u` in
476`u3j_core`); `lab`, the complete label path; and `jit`, any
477other dynamic data that may speed up execution.
479We construct `hap`, when we create the calx, by iterating through
480the arms registered in the `u3j_core`. Note the way a `u3j_harm`
481declares itself, with the string `fcs_c` which can contain either
482an axis or a name. Most jetted cores are of course gates, which
483have one formula at one axis within the core: `fcs_c` is `".3"`.
485But we do often have fast cores with more complex arm structure,
486and it would be sad to have to manage their axes by hand. To use
487an `fcs_c` with a named arm, it's sufficient to make sure the
488name is bound to a formula `[0 axis]` in the hook table.
490`jit`, as its name suggests, is a stub where any sort of
491optimization data computed on battery registration might go. To
492use it, fill in the `_cj_jit()` function.
494### u3j: the hot dashboard
496Now it should be easy to see how we actually invoke jets. Every
497time we run a nock `9` instruction (pretty often, obviously), we
498have a core and an axis. We pass these to `u3j_kick()`, which
499will try to execute them.
501Because nouns with a reference count of 1 are precious,
502`u3j_kick()` has a tricky reference control definition. It
503reserves the right to return `u3_none` in the case where there is
504no driver, or the driver does not apply for this case; in this
505case, it retains argument `cor`. If it succeeds, though, it
508`u3j_kick()` searches for the battery (always the head of the
509core, of course) in the hot dashboard. If the battery is
510registered, it searches for the axis in `hap` in the `calx`.
511If it exists, the core matches a driver and the driver jets this
512arm. If not, we return `u3_none`.
514Otherwise, we call `fun_f` in our `u3j_harm`. This obeys the
515same protocol as `u3j_kick()`; it can refuse to function by
516returning `u3_none`, or consume the noun.
518Besides the actual function pointer `fun_f`, we have some flags
519in the `u3j_harm` which tell us how to call the arm function.
521If `ice` is yes (`&`, `0`), the jet is known to be perfect and we
522can just trust the product of `fun_f`. Otherwise, we need to run
523**both** the Nock arm and `fun_f`, and compare their results.
525(Note that while executing the C side of this test, we have to
526set `ice` to yes; on the Nock side, we have to set `liv` to no.
527Otherwise, many non-exponential functions become exponential.
528When auto-testing jets in this way, the principle is that the
529test is on the outermost layer of recursion.)
531(Note also that anyone who multi-threads this execution
532environment has a slight locking problem with these flags if arm
533testing is multi-threaded.)
535If `tot` is yes, (`&`, `0`), the arm function is **total** and has
536to return properly (though it can still return **u3_none**).
537Otherwise, it is **partial** and can `u3_cm_bail()` out with
538c3__punt. This feature has a cost: the jet runs in a subroad.
540Finally, if `liv` is no (`|`, 1), the jet is off and doesn't run.
542It should be easy to see how the tree of cores gets declared -
543precisely, in `j/dash.c`. We declare the hierarchy as a tree
544of `u3j_core` structures, each of which comes with a static list
545of arms `arm_u` and sub-cores `dev_u`.
547In `u3j_boot()`, we traverse the hierarchy, fill in parent
548pointers `par_u`, and enumerate all `u3j_core` structures
549into a single flat array `u3j_dash.ray_u`. Our hot state
550then appears ready for action.
552### u3j: jet functions
554At present, all drivers are compiled statically into `u3`. This is
555not a long-term permanent solution or anything. However, it will
556always be the case with a certain amount of core functionality.
558For instance, there are some jet functions that we need to call
559as part of loading the Arvo kernel - like `++cue` to unpack a
560noun from an atom. And obviously it makes sense, when jets are
561significant enough to compile into `u3`, to export their symbols
562in headers and the linker.
564There are three interface prefixes for standard jet functions:
565`u3k`, `u3q`, and `u3w`. All jets have `u3w` interfaces; most
566have `u3q`; some have `u3k`. Of course the actual logic is
569`u3w` interfaces use the same protocol as `fun_f` above: the
570caller passes the entire core, which is retained if the function
571returns `u3_none`, transferred otherwise. Why? Again, use
572counts of 1 are special and precious for performance hackers.
574`u3q` interfaces break the core into C arguments, **retain** noun
575arguments, and **transfer** noun returns. `u3k` interfaces are the
576same, except with more use of `u3_none` and other simple C
577variations on the Hoon original, but **transfer** both arguments
578and returns. Generally, `u3k` are most convenient for new code.
580Following `u3k/q/w` is `[a-f]`, corresponding to the 6 logical
581tiers of the kernel, or `g` for user-level jets. Another letter
582is added for functions within subcores. The filename, under
583`j/`, follows the tier and the function name.
585For instance, `++add` is `u3wa_add(cor)`, `u3qa_add(a, b)`, or
586`u3ka_add(a, b)`, in `j/a/add.c`. `++get` in `++by` is
587`u3wdb_get(cor)`, `u3kdb_get(a, b)`, etc, in `j/d/by_get.c`.
589For historical reasons, all internal jet code in `j/[a-f]`
590**retains** noun arguments, and **transfers** noun results. Please
591do not do this in new `g` jets! The new standard protocol is to
592transfer both arguments and results.
594### u3a: allocation functions
596`u3a` allocates on the current road (u3R). Its internal
597structures are uninteresting and typical of a naive allocator.
599The two most-used `u3a` functions are `u3a_gain()` to add a
600reference count, and `u3a_lose()` to release one (and free the
601noun, if the use count is zero). For convenience, `u3a_gain()`
602returns its argument. The pair are generally abbreviated with
603the macros `u3k()` and `u3z()` respectively.
605Normally we create nouns through `u3i` functions, and don't call
606the `u3a` allocators directly. But if you do:
608One, there are **two** sets of allocators: the word-aligned
609allocators and the fully-aligned (ie, malloc compatible)
610allocators. For instance, on a typical OS X setup, malloc
611produces 16-byte aligned results - needed for some SSE
614These allocators are **not compatible**. For 32-bit alignment
615as used in nouns, call
618 /* u3a_walloc(): allocate storage measured in words.
621 u3a_walloc(c3_w len_w);
623 /* u3a_wfree(): free storage.
626 u3a_wfree(void* lag_v);
628 /* u3a_wealloc(): word realloc.
631 u3a_wealloc(void* lag_v, c3_w len_w);
634For full alignment, call:
637 /* u3a_malloc(): aligned storage measured in bytes.
640 u3a_malloc(size_t len_i);
642 /* u3a_realloc(): aligned realloc in bytes.
645 u3a_realloc(void* lag_v, size_t len_i);
647 /* u3a_realloc2(): gmp-shaped realloc.
650 u3a_realloc2(void* lag_v, size_t old_i, size_t new_i);
652 /* u3a_free(): free for aligned malloc.
655 u3a_free(void* tox_v);
657 /* u3a_free2(): gmp-shaped free.
660 u3a_free2(void* tox_v, size_t siz_i);
663There are also a set of special-purpose allocators for building
664atoms. When building atoms, please remember that it's incorrect
665to have a high 0 word - the word length in the atom structure
666must be strictly correct.
668Of course, we don't always know how large our atom will be.
669Therefore, the standard way of building large atoms is to
670allocate a block of raw space with `u3a_slab()`, then chop off
671the end with `u3a_malt()` (which does the measuring itself)
672or `u3a_mint()` in case you've measured it yourself.
674Once again, **do not call `malloc()`** (or C++ `new`) within any
675code that may be run within a jet. This will cause rare sporadic
676corruption when we interrupt execution within a `malloc()`. We'd
677just override the symbol, but `libuv` uses `malloc()` across
678threads within its own synchronization primitives - for this to
679work with `u3a_malloc()`, we'd have to introduce our own locks on
680the surface-level road (which might be a viable solution).
682### u3n: nock execution
684The `u3n` routines execute Nock itself. On the inside, they have
685a surprising resemblance to the spec proper (the only interesting
686detail is how we handle tail-call elimination) and are, as one
687would expect, quite slow. (There is no such thing as a fast tree
690There is only one Nock, but there are lots of ways to call it.
691(Remember that all `u3n` functions **transfer** C arguments and
694The simplest interpreter, `u3n_nock_on(u3_noun bus, u3_noun fol)`
695invokes Nock on `bus` (the subject) and `fol` (the formula).
696(Why is it`[subject formula]`, not `[formula subject]`? The same
697reason `0` is true and `1` is false.)
699A close relative is `u3n_slam_on(u3_noun gat, u3_noun sam)`,
700which slams a **gate** (`gat`) on a sample (`sam`). (In a normal
701programming language which didn't talk funny and was retarded,
702`u3n_slam_on()` would call a function on an argument.) We could
703write it most simply as:
707 u3n_slam_on(u3_noun gat, u3_noun sam)
709 u3_noun pro = u3n_nock_on
711 u3nc(sam, u3k(u3t(u3t(gat))))),
718Simpler is `u3n_kick_on(u3_noun gat)`, which slams a gate (or,
719more generally, a **trap** - because sample structure is not even
720needed here) without changing its sample:
724 u3n_kick_on(u3_noun gat, u3_noun sam)
726 return u3n_nock_on(gat, u3k(u3h(gat)));
730The `_on` functions in `u3n` are all defined as pure Nock. But
731actually, even though we say we don't extend Nock, we do. But we
734Note that `u3` has a well-developed error handling system -
735`u3m_bail()` to throw an exception, `u3m_soft_*` to catch one.
736But Nock has no exception model at all. That's okay - all it
737means if that if an `_on` function bails, the exception is an
738exception in the caller.
740However, `u3`'s exception handling happens to match a convenient
741virtual super-Nock in `hoon.hoon`, the infamous `++mock`. Of
742course, Nock is slow, and `mock` is Nock in Nock, so it is
743(logically) super-slow. Then again, so is decrement.
745With the power of `u3`, we nest arbitrary layers of `mock`
746without any particular performance cost. Moreover, we simply
747treat Nock proper as a special case of `mock`. (More precisely,
748the internal VM loop is `++mink` and the error compiler is
749`++mook`. But we call the whole sandbox system `mock`.)
751The nice thing about `mock` functions is that (by executing
752within `u3m_soft_run()`, which as you may recall uses a nested
753road) they provide both exceptions and the namespace operator -
754`.^` in Hoon, which becomes operator `11` in `mock`.
756`11` requires a namespace function, or `fly`, which produces a
757`++unit` - `~` (`0`) for no binding, or `[0 value]`. The sample
758to a `fly` is a `++path`, just a list of text `span`.
760`mock` functions produce a `++toon`. Fully elaborated:
763 ++ noun ,* :: any noun
764 ++ path (list ,@ta) :: namespace path
765 ++ span ,@ta :: text-atom (ASCII)
766 ++ toon $% [%0 p=noun] :: success
767 [%1 p=(list path)] :: blocking paths
768 [%2 p=(list tank)] :: stack trace
771 $% [%leaf p=tape] :: flat text
772 $: %palm :: backstep list
773 p=[p=tape q=tape r=tape s=tape] :: mid cap open close
774 q=(list tank) :: contents
776 $: %rose :: straight list
777 p=[p=tape q=tape r=tape] :: mid open close
778 q=(list tank) :: contents
783(Note that `tank` is overdesigned and due for replacement.)
785What does a `toon` mean? Either your computation succeded (`[0
786noun]`, or could not finish because it blocked on one or more
787global paths (`[1 (list path)]`), or it exited with a stack trace
790Note that of all the `u3` exceptions, only `%exit` is produced
791deterministically by the Nock definition. Therefore, only
792`%exit` produces a `2` result. Any other argument to
793`u3m_bail()` will unwind the virtualization stack all the way to
794the top - or to be more exact, to `u3m_soft_top()`.
796In any case, the simplest `mock` functions are `u3n_nock_un()`
797and `u3n_slam_un()`. These provide exception control without
798any namespace change, as you can see by the code:
801 /* u3n_nock_un(): produce .*(bus fol), as ++toon.
804 u3n_nock_un(u3_noun bus, u3_noun fol)
806 u3_noun fly = u3nt(u3nt(11, 0, 6), 0, 0); // |=(a=* .^(a))
808 return u3n_nock_in(fly, bus, fol);
811 /* u3n_slam_un(): produce (gat sam), as ++toon.
814 u3n_slam_un(u3_noun gat, u3_noun sam)
816 u3_noun fly = u3nt(u3nt(11, 0, 6), 0, 0); // |=(a=* .^(a))
818 return u3n_slam_in(fly, gat, sam);
822The `fly` is added as the first argument to `u3n_nock_in()` and
823`u3n_slam_in()`. Of course, logically, `fly` executes in the
824caller's exception layer. (Maintaining this illusion is slightly
825nontrivial.) Finally, `u3n_nock_an()` is a sandbox with a null
830The only `u3e` function you should need to call is `u3e_save()`,
831which saves the loom. As it can be restored on any platform,
832please make sure you don't have any state in the loom that is
833bound to your process or architecture - except for exceptions
834like the warm jet state, which is actively purged on reboot.
836### u3r: reading nouns (weak)
838As befits accessors they don't make anything, `u3r` noun reading
839functions always retain their arguments and their returns. They
840never bail; rather, when they don't work, they return a `u3_weak`
843Most of these functions are straightforward and do only what
844their comments say. A few are interesting enough to discuss.
846`u3r_at()` is the familiar tree fragment function, `/` from the
847Nock spec. For taking complex nouns apart, `u3r_mean()` is a
848relatively funky way of deconstructing nouns with a varargs list
849of `axis`, `u3_noun *`. For cells, triples, etc, decompose with
850`u3r_cell()`, `u3r_trel()`, etc. For the tagged equivalents, use
851`u3r_pq()` and friends.
853`u3r_sing(u3_noun a, u3_noun b)` (true if `a` and `b` are a
854**single** noun) are interesting because it uses mugs to help it
855out. Clearly, different nouns may have the same mug, but the
856same nouns cannot have a different mug. It's important to
857understand the performance characteristics of `u3r_sing()`:
858the worst possible case is a comparison of duplicate nouns,
859which have the same value but were created separately. In this
860case, the tree is traversed
862`u3r_sung()` is a deeply funky and frightening version of
863`u3r_sing()` that unifies pointers to the duplicate nouns it
864finds, freeing the second copy. Obviously, do not use
865`u3r_sung()` when you have live, but not reference counted, noun
866references from C - if they match a noun with a refcount of 1
867that gets freed, bad things happen.
869It's important to remember that `u3r_mug()`, which produces a
87031-bit, nonzero insecure hash, uses the `mug_w` slot in any boxed
871noun as a lazy cache. There are a number of variants of
872`u3r_mug()` that can get you out of building unneeded nouns.
874### u3x: reading nouns (bail)
876`u3x` functions are like `u3r` functions, but instead of
877returning `u3_none` when (for instance) we try to take the head
878of an atom, they bail with `%exit`. In other words, they do what
879the same operation would do in Nock.
883We can of course use the Hoon `map` structure as an associative
884array. This is a balanced treap and reasonably fast. However,
885it's considerably inferior to a custom structure like an HAMT
886(hash array-mapped trie). We use `u3_post` to allocate HAMT
887structures on the loom.
889(Our HAMT implements the classic Bagwell algorithm which depends
890on the `gcc` standard directive `__builtin_popcount()`. On a CPU
891which doesn't support popcount or an equivalent instruction, some
892other design would probably be preferable.)
894There's no particular rocket science in the API. `u3h_new()`
895creates a hashtable; `u3h_free()` destroys one; `u3h_put()`
896inserts, `u3h_get()` retrieves. You can transform values in a
897hashtable with `u3h_walk()`.
899The only funky function is `u3h_gut()`, which unifies keys with
900`u3r_sung()`. As with all cases of `u3r_sung()`, this must be
901used with extreme caution.
905Connected to the `~+` rune in Hoon, via the Nock `%memo` hint,
906the memoization facility is a general-purpose cache.
908(It's also used for partial memoization - a feature that'll
909probably be removed, in which conservative worklist algorithms
910(which would otherwise be exponential) memoize everything in the
911subject **except** the worklist. This is used heavily in the Hoon
912compiler jets (j/f/*.c). Unfortunately, it's probably not
913possible to make this work perfectly in that it can't be abused
914to violate Nock, so we'll probably remove it at a later date,
915instead making `++ut` keep its own monadic cache.)
917Each `u3z` function comes with a `c3_m` mote which disambiguates
918the function mapping key to value. For Nock itself, use 0. For
919extra speed, small tuples are split out in C; thus, find with
922 u3_weak u3z_find(c3_m, u3_noun);
923 u3_weak u3z_find_2(c3_m, u3_noun, u3_noun);
924 u3_weak u3z_find_3(c3_m, u3_noun, u3_noun, u3_noun);
925 u3_weak u3z_find_4(c3_m, u3_noun, u3_noun, u3_noun, u3_noun);
931 u3_noun u3z_save(c3_m, u3_noun, u3_noun);
932 u3_noun u3z_save_2(c3_m, u3_noun, u3_noun, u3_noun);
933 u3_noun u3z_save_3(c3_m, u3_noun, u3_noun, u3_noun, u3_noun);
934 u3_noun u3z_save_4(c3_m, u3_noun, u3_noun, u3_noun, u3_noun, u3_noun);
937where the value is the last argument. To eliminate duplicate
945`u3z` functions retain keys and transfer values.
947The `u3z` cache, built on `u3h` hashes, is part of the current
948road, and goes away when it goes away. (In future, we may wish
949to promote keys/values which outlive the road, as we do with jet
950state.) There is no cache reclamation at present, so be careful.
952### u3t: tracing and profiling.
956### u3v: the Arvo kernel
958An Arvo kernel - or at least, a core that compiles with the Arvo
959interface - is part of the global `u3` state. What is an Arvo
960core? Slightly pseudocoded:
965 ++ come |/ {yen/@ ova/(list ovum) nyf/pone} :: 11
968 ++ keep |/ {now/@da hap/path} :: 4
971 ++ load |/ {yen/@ ova/(list ovum) nyf/pane} :: 86
974 ++ peek |/ {now/@da path} :: 87
977 ++ poke |/ {now/@da ovo/ovum} :: 42
980 ++ wish |/ txt/@ta :: 20
984 ++ card {p/@tas q/*} :: typeless card
985 ++ ovum {p/wire q/card} :: Arvo event
986 ++ wire path :: event cause
989This is the Arvo ABI in a very real sense. Arvo is a core with
990these six arms. To use these arms, we hardcode the axis of the
991formula (`11`, `4`, `86`, etc) into the C code that calls Arvo,
992because otherwise we'd need type metadata - which we can get, by
995It's important to understand the Arvo event/action structure, or
996`++ovum`. An `ovum` is a `card`, which is any `[term noun]`
997cell, and a `++wire`, a `path` which indicates the location of
998the event. At the Unix level, the `wire` corresponds to a system
999module or context. For input events, this is the module that
1000caused the event; for output actions, it's the module that
1003`++poke` sends Arvo an event `ovum`, producing a cell of action
1004ova and a new Arvo core.
1006`++peek` dereferences the Arvo namespace. It takes a date and a
1007key, and produces `~` (`0`) or `[~ value]`.
1009`++keep` asks Arvo the next time it wants to be woken up, for the
1010given `wire`. (This input will probably be eliminated in favor
1011of a single global timer.)
1013`++wish` compiles a string of Hoon source. While just a
1014convenience, it's a very convenient convenience.
1016`++come` and `++load` are used by Arvo to reset itself (more
1017precisely, to shift the Arvo state from an old kernel to a new
1018one); there is no need to call them from C.
1020Now that we understand the Arvo kernel interface, let's look at
1021the `u3v` API. As usual, all the functions in `u3v` are
1022commented, but unfortunately it's hard to describe this API as
1023clean at present. The problem is that `u3v` remains design
1024coupled to the old `vere` event handling code written for `u2`.
1025But let's describe the functions you should be calling, assuming
1026you're not writing the next event system. There are only two.
1028`u3v_wish(str_c)` wraps the `++wish` functionality in a cache
1029(which is read-only unless you're on the surface road).
1031`u3v_do()` uses `wish` to provide a convenient interface for
1032calling Hoon kernel functions by name. Even more conveniently,
1033we tend to call `u3v_do()` with these convenient aliases:
1036 #define u3do(txt_c, arg) u3v_do(txt_c, arg)
1037 #define u3dc(txt_c, a, b) u3v_do(txt_c, u3nc(a, b))
1038 #define u3dt(txt_c, a, b, c) u3v_do(txt_c, u3nt(a, b, c))
1039 #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