10As we noted, the definition of `span` in the last chapter is a
11subset. The Hoon type system is simple, but not *that* simple.
13The good news is that you can *skip this section*, at least when
14you're first learning Hoon. Polymorphism and aliasing are mainly
15for advanced programmers writing complex infrastructure and/or
16large functions. Don't worry about them for a little while.
18## The complete, uncensored `span`
23 == $% {$atom p/term q/(unit atom)}
26 {$face p/$@(term tune) q/span}
31++ coil $: p/?($gold $iron $lead $zinc)
33 r/{p/?($~ ^) q/(map term foot)}
35++ foot $% {$dry p/twig}
39 $: p/(map term (unit twig))
44If you compare this to the basic `span`, you'll see that we've
45changed two stems: `$core` and `$face`. We added polymorphism to
46`$core`, aliases and bridges to `$face`.
48## `$core`: advanced polymorphism
50If cores never changed, we wouldn't need polymorphism. Of
51course, nouns are immutable and never change, but we use them as
52templates to construct new nouns around.
54Suppose we take a core, a cell `{battery payload}`, and replace
55the payload with a different noun. Then, we invoke an arm from
58Is this legal? Does it make sense? Every function call in Hoon
59does this, so we'd better make it work well.
61The full core stores both payload spans. The span that describes
62the payload currently in the core is `p`. The span that describes
63the payload the core was compiled with is `q.q`.
65In the Bertrand Meyer tradition of type theory, there are two
66forms of polymorphism: variance and genericity. In Hoon this
67choice is per arm, which is why our battery went from `(map term
68twig)` to `(map term foot)` when it went into the coil. A `foot`
69is `$dry` or `$wet`. Dry polymorphism relies on variance; wet
70polymorphism relies on genericity.
74For a dry arm, we apply the Liskov substitution principle: we
75ask, "can we use any `p` as if it was a `q.q`?" This is the same
76test as in `:cast` or any type comparison (`nest`). Intuitively,
77we ask: "is the new payload compatible with the old payload?"
79For a core `a`, if `p.a` fits in `q.q.a`, we can use arms on the
80core. The product of a dry arm whose twig is `b` is always
81defined as `{$hold a(p q.q.a, p.q %gold) b}`.
83In other words, the subject of the arm computation is the
84original core span. The span of the modified payload, `p`, is
85thrown away. This is of course the normal behavior of a function
86call in most languages.
88### Dry polymorphism and core nesting rules
90Dry polymorphism works by substituting cores. Typically, the
91the programmer uses one core as an interface definition, then
92replaces it with another core which does something useful.
94For core `b` to nest within core `a`, the batteries of `a` and
95`b` must have the same tree shape, and the product of each `b`
96arm must nest within the product of the `a` arm. Wet arms (see
97below) are not compatible unless the twig is exactly the same.
99But we also apply a payload test that depends on the rules of
100variance. Again, this is traditional Meyer type theory; don't
101worry if you don't know it. These rules should make sense if you
102just think about them intuitively.
104Each core has a "metal" `p.q` which defines its variance model.
105A core can be *invariant* (`$gold`), *bivariant* (`$lead`),
106*covariant* (`$zinc`), or *contravariant* (`$iron`). The default
107is gold; a gold core can be cast or converted to any metal, and
108any metal can be cast or converted to lead.
110A gold core `a` has a read-write payload; another core `b` that
111nests within it (i.e., can be substituted for it) must be a gold
112core whose payload is mutually compatible (`+3.a` nests in `+3.b`,
113`+3.b` nests in `+3.a`). Hence, *invariant*.
115A lead core `a` has an opaque payload. There is no constraint on
116the payload of a core `b` which nests within it. Hence,
119An iron core `a` has a write-only sample (payload head, `+6.a`)
120and an opaque context (payload tail, `+7.a`). A core `b` which
121nests within it must be a gold or iron core, such that `+6.a`
122nests within `+6.b`. Hence, *contravariant*.
124A zinc core `a` has a read-only sample (payload head, `+6.a`)
125and an opaque context (payload tail, `+7.a`). A core `b` which
126nests within it must be a gold or zinc core, such that `+6.b`
127nests within `+6.a`. Hence, *covariant*.
131For a wet arm, we ask: "suppose this core was actually compiled
132using `p` instead of `q.q`?" Would the Nock formula we generated
133for `q.q` actually work for a `p` payload?
135A wet arm with twig `b` in core `a` produces the span `{$hold a
136b}`. Assuming `a` has a modified payload, this will (lazily)
137analyze the arm with the caller's modified payload, `p`.
139But of course, we don't actually recompile the arm at runtime.
140We actually run the formula generated for the original payload,
143When we call a wet arm, we're essentially using the twig as a
144macro. We are not generating new code for every call site; we
145are creating a new type analysis path, which works as if we
146expanded the callee with the caller's context.
148Consider a function like `turn` (Haskell `map`) which transforms
149each element of a list. To use `turn`, we install a list and a
150transformation function in a generic core. The span of the list
151we produce depends on the span of the list and the span of the
152transformation function. But the Nock formulas for transforming
153each element of the list will work on any function and any list,
154so long as the function's argument is the list item.
156Again, will this work? A simple (and not quite right) way to ask
157this question is to compile all the twigs in the battery for both
158a payload of `p` and a payload of `q.q`, and see if they generate
159exactly the same Nock. The actual algorithm is a little more
160interesting, but not much.
162A Haskeller might say that in a sense, `q.q` and `q.r.q` (the
163original payload and the battery) define a sort of implicit
164typeclass. And indeed, Hoon uses wet arms for the same kinds of
165problems as Haskell typeclasses.
167Like typeclasses, wet arms / gates are a powerful tool. Don't
168use them unless you know what you're doing.
172There's only one field of the `coil` we haven't explained yet:
173`p.r.q`. This is simply the compiled battery, if available. (Of
174course, we compile the twigs in a core against the core itself,
175and the formulas can't be available while we're compiling them.)
176External users of the core want this battery constant, though: it
177lets us fold constants by executing arms at compile time.
179## `$face`: aliases and bridges
181In the advanced `tune` form, the `$face` span also has a couple
182of secret superpowers for hacking the namespace. Remember that
183Hoon doesn't have anything like a symbol table; to resolve a
184limb, we just search the span depth-first.
186If a name is in the `p.p` map, it's an alias. The map contains a
187`(unit twig)`; if the unit is full, the name resolves to that
188twig (compiled against the `q` span). If the unit is empty,
189the name is blocked / skipped (see [limb](../twig/limb) for what
192If a name is in the `q.p` map, it's a bridge. When we search for
193a name, we also compile the bridge, and check if the name
194resolves against the bridge product. If so, we use it.
196These hacks let us deal with interesting large-scale problems,
197like complex library dependencies. You definitely don't need to
198understand them to just write simple Hoon programs.
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