@@ -0,0 +1,201 @@
4+This document is complemented by the source code of applications that use the new gall model (like hall), but doesn't require it. Code snippets and diagrams will be provided where useful. Some knowledge of the functioning of Hoon apps is assumed.
6+Naming of functions and molds in this document is tentative.
14+Imagine a `++poke`, `++diff`, or even a `++peer` arm. In current implementations, you'll often see their product defined through cast as the following:
16+Or (sadly) more commonly:
17+`^- {(list move) _+>.$}`
19+These arms are producing both side-effects and new state. To do this, the application has to simultaneously figure out "how does this poke/diff/whatever change our state?" and "what side-effects does this change have?". You'll find that the logic relating to these two often becomes tangled, making the flow of the whole more difficult to understand. Gall currently does not help here.
22+What if we could separate these two, untangling state changes from side effects? And if we plucked state changes apart into *calculating* and *applying* the change, wouldn't we be able to deduce side-effects from calculated changes? Enter New Gall.
24+@TODO better argumentation, lay out advantages of change, etc.
26+State gets changed, subscriptions need to be updated to reflect that, etc.
28+Queries: we're just getting data (and maybe staying up to date on it)!
33+This wouldn't be Urbit if we had some cool new terminology for people to learn. These aren't super obscure though, don't worry.
35+`brain`: application state.
36+`delta`: change to application state.
37+`opera`: side-effect, operation, move.
38+`query`: request (to an app) for data on a specific path.
39+`prize`: query result.
40+`rumor`: change to a query result.
42+These should be fairly straightforward. The idea of `brain`+`delta` and `prize`+`rumor` is comparable to Urbit's "state is a function of facts" way of thinking. If I diligently update the `prize` I got using the `rumor`s, then I won't ever need to `query` again to pull down the full correct `prize`.
44+This also wouldn't be Urbit if we couldn't boil that down to a few simple functions. Let's try, shall we?
47+++ bake |= {brain delta} ^- {brain opera}
48+++ peek |= {brain query} ^- (unit (unit prize))
49+++ feel |= {query delta} ^- (unit rumor)
50+++ gain |= {prize rumor} ^- prize
53+The first three are part of new gall. `++gain` usually won't be implemented in that exact way, but needs to be possible still, to illustrate our functional definitions. The following two statements should be equal, for any given brain, query and delta:
56+(peek brain:(bake brain delta) query)
57+(gain rumor:(feel query delta) (peek brain query))
65+To help solidify this, let's look at a very simple example app. We'll be making a counter that can go up and down. (The app code in its entirety can be found [here](gall-eg.hoon).)
66+Below is the application state, and the deltas we'll be using to modify it.
70+++ brain {num/@ud $~} :< application state
71+++ delta :> state change
72+ $% {$increment $~} :< +1
73+ {$decrement $~} :< -1
77+Fairly simple, right? We store a number, and every change either in- or decrements it.
81+Let's also define the queries we'll be supporting, their results, and the changes to their results. Instead of only allowing people to query the current number, let's make it a tad more interesting by allowing them to query whether or not the current number is a multiple of something.
84+++ query :> valid queries
85+ $% {$number $~} :< current number
86+ {$mul-of val/@ud} :< is num multiple of?
88+++ prize :> query results
89+ $% {$number num/@ud} :< /number
90+ {$mul-of mul/?} :< /mul-of
92+++ rumor :> query result changes
93+ $% {$number delta} :< /number
94+ {$mul-of mul/?} :< /mul-of
99+You'll see the `rumor` for a `/number` query just contains a delta. In this specific case, a change in state maps directly to a change to `prize`. For `/mul-of` queries, things aren't that simple.
103+Now, let's implement all the arms that will be called as things start happening. First, we want to implement `++leak`, which checks if a given `ship` is allowed to make a certain `query`. In this case, we're fine with everyone checking out our application's data.
108+++ leak :> read permission
109+ |= {who/ship qer/query}
111+ & :: everyone's allowed
114+Next, let's create the `prize`s for the folks that make it through `++leak`. We'll be producing a `(unit (unit prize))` so we can potentially say `~` for "unavailable" and `[~ ~]` for "invalid query".
117+++ peek :> synchronous read
119+ ^- (unit (unit prize))
121+ $number ``[%number num]
122+ $mul-of ?: =(0 mul.qer) [~ ~]
123+ ``[%mul-of =(0 (mod num mul.qer))]
127+Our app has been queried! If they're interested enough, they'll want to receive updates on that whenever relevant state changes. First, let's see how state changes happen. We still have regular old `++poke` arms, let's use that to prompt our application to change its state.
130+++ poke-loob :> regular old poke
134+ ?: inc [%increment ~]
137+++ bake :> apply delta to state
142+ $increment +>(num +(num))
143+ $decrement +>(num (dec num))
147+`delta`s get redirected into `++bake` so that our state can get changed as described. After this happens the same `delta`s get, for each active query, routed into `++feel`. That then figures out what the `rumor` relevant to the query is, if applicable.
150+++ feel :> delta to rumor
151+ |= {qer/query del/delta}
158+ :: since we only want to send a rumor if result
159+ :: changed, we need to deduce the old state from
160+ :: the current state and the delta. depends on the
161+ :: fact that state changes before ++feel. funky!
166+ $increment (dec num)
170+ =/ new =(0 (mod num mul.qer))
176+As we saw earlier, for `/number` queries the `delta`s match one-on-one with the relevant `rumor`s. For `/mul-of` queries we do a little bit of work to see if anything actually changed. No need to send a `rumor` if it hasn't.
181+These arms get chained together by gall. `++cope`(?) can be implemented to accept `prize`s. `++hear`(?) can be implemented to accept `rumor`s.
182+@TODO probably show examples?
184+One thing that wasn't shown was a `++path-to-query` arm. This is responsible for converting a query path to a query object as defined in the application's structures.
189+`coin`s are a pain to work with. The "coin list parsing library" makes this a bit easier, but ideally we'd just define a `++path-to-query` arm and do everything as described above.
191+Why do prize units mean what they do? Would've expected it the other way around.
193+Not sure if I'm a fan of "in reverse order". It's technically easier, but semantically yet another thing to keep in mind. Luckily, most of the "add to x, produce all of it later" code is set-and-forget, so probably not a huge deal.
195+`++bake` should just produce `(quip move +>)`. why would we want to be forced to keep operas in state? just to avoid [~ +>] over +>?
197+weirdness wrt relying on (old) state in ++feel
199+gall should probably cache `++feel` results during a single pass. (but then for eg /circle/nom/13 and /circle/nom/14 it would still recalculate. better than nothing though.)
201+Lots of cruft for small applications to bother with. (Though I admit it does feel like structured cruft, at least.)