on master help sign in
First draft of gall docs.
69502a4Fang
docs/talk/gall-eg.hoonnew file+84
@@ -0,0 +1,84 @@
1+:: counter app
2+:: new gall example, may or may not compile.
3+::
4+|%
5+++ query :> valid queries
6+ $% {$number $~} :< current number
7+ {$mul-of val/@ud} :< is num multiple of?
8+ == ::
9+++ prize :> query results
10+ $% {$number num/@ud} :< /number
11+ {$mul-of mul/?} :< /mul-of
12+ == ::
13+++ rumor :> query result changes
14+ $% {$number delta} :< /number
15+ {$mul-of mul/?} ::TODO or just flip? :< /mul-of
16+ == ::
17+:: the above would ordinarily be placed in sur/, ::
18+:: since other apps might want to use it. ::
19+++ brain {num/@ud $~} :< application state
20+++ delta :> state change
21+ $% {$increment $~} :< +1
22+ {$decrement $~} :< -1
23+ == ::
24+--
25+::
26+|_ {bol/bowl brain}
27+::
28+++ leak :> read permission
29+ |= {who/ship qer/query}
30+ ^- ?
31+ & :: everyone's allowed
32+::
33+++ peek :> synchronous read
34+ |= qer/query
35+ ^- (unit (unit prize))
36+ ?- -.qer
37+ $number ``[%number num]
38+ $mul-of ?: =(0 mul.qer) [~ ~]
39+ ``[%mul-of =(0 (mod num mul.qer))]
40+ ==
41+::
42+++ poke-loob :> regular old poke
43+ |= inc/?
44+ ^- (list delta)
45+ :_ ~
46+ ?: inc [%increment ~]
47+ [%decrement ~]
48+::
49+++ bake :> apply delta to state
50+ |= del/delta
51+ ^- (quip opera +>)
52+ :- ~
53+ ?- -.del
54+ $increment +>(num +(num))
55+ $decrement +>(num (dec num))
56+ ==
57+::
58+++ feel :> delta to rumor
59+ |= {qer/query del/delta}
60+ ^- (unit rumor)
61+ ?- -.qer
62+ $number
63+ `[%number del]
64+ ::
65+ $mul-of
66+ :: since we only want to send a rumor if result
67+ :: changed, we need to deduce the old state from
68+ :: the current state and the delta. depends on the
69+ :: fact that state changes before ++feel. funky!
70+ =/ old
71+ .= 0
72+ %+ mod
73+ ?- -.del
74+ $increment (dec num)
75+ $decrement +(num)
76+ ==
77+ mul.qer
78+ =/ new =(0 (mod num mul.qer))
79+ ?: =(old new) ~
80+ `[%mul-of new]
81+ ==
82+::
83+:: not pictured: ++path-to-query
84+--
docs/talk/gall.mdnew file+201
@@ -0,0 +1,201 @@
1+
2+# The Gall Delta Model
3+
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.
5+
6+Naming of functions and molds in this document is tentative.
7+
8+## Table of contents
9+
10+@TODO
11+
12+## Introduction
13+
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:
15+`^- (quip move +>)`
16+Or (sadly) more commonly:
17+`^- {(list move) _+>.$}`
18+
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.
20+
21+@TODO better segue
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.
23+
24+@TODO better argumentation, lay out advantages of change, etc.
25+
26+State gets changed, subscriptions need to be updated to reflect that, etc.
27+
28+Queries: we're just getting data (and maybe staying up to date on it)!
29+
30+
31+## Structure
32+
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.
34+
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.
41+
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`.
43+
44+This also wouldn't be Urbit if we couldn't boil that down to a few simple functions. Let's try, shall we?
45+
46+```
47+++ bake |= {brain delta} ^- {brain opera}
48+++ peek |= {brain query} ^- (unit (unit prize))
49+++ feel |= {query delta} ^- (unit rumor)
50+++ gain |= {prize rumor} ^- prize
51+```
52+
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:
54+
55+```
56+(peek brain:(bake brain delta) query)
57+(gain rumor:(feel query delta) (peek brain query))
58+```
59+
60+
61+## By example
62+
63+### State & deltas
64+
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.
67+
68+```
69+|%
70+++ brain {num/@ud $~} :< application state
71+++ delta :> state change
72+ $% {$increment $~} :< +1
73+ {$decrement $~} :< -1
74+ == ::
75+```
76+
77+Fairly simple, right? We store a number, and every change either in- or decrements it.
78+
79+### Queries & rumors
80+
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.
82+
83+```
84+++ query :> valid queries
85+ $% {$number $~} :< current number
86+ {$mul-of val/@ud} :< is num multiple of?
87+ == ::
88+++ prize :> query results
89+ $% {$number num/@ud} :< /number
90+ {$mul-of mul/?} :< /mul-of
91+ == ::
92+++ rumor :> query result changes
93+ $% {$number delta} :< /number
94+ {$mul-of mul/?} :< /mul-of
95+ == ::
96+--
97+```
98+
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.
100+
101+### Functionality
102+
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.
104+
105+```
106+|_ {bol/bowl brain}
107+::
108+++ leak :> read permission
109+ |= {who/ship qer/query}
110+ ^- ?
111+ & :: everyone's allowed
112+```
113+
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".
115+
116+```
117+++ peek :> synchronous read
118+ |= qer/query
119+ ^- (unit (unit prize))
120+ ?- -.qer
121+ $number ``[%number num]
122+ $mul-of ?: =(0 mul.qer) [~ ~]
123+ ``[%mul-of =(0 (mod num mul.qer))]
124+ ==
125+```
126+
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.
128+
129+```
130+++ poke-loob :> regular old poke
131+ |= inc/?
132+ ^- (list delta)
133+ :_ ~
134+ ?: inc [%increment ~]
135+ [%decrement ~]
136+::
137+++ bake :> apply delta to state
138+ |= del/delta
139+ ^- (quip opera +>)
140+ :- ~
141+ ?- -.del
142+ $increment +>(num +(num))
143+ $decrement +>(num (dec num))
144+ ==
145+```
146+
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.
148+
149+```
150+++ feel :> delta to rumor
151+ |= {qer/query del/delta}
152+ ^- (unit rumor)
153+ ?- -.qer
154+ $number
155+ `[%number del]
156+ ::
157+ $mul-of
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!
162+ =/ old
163+ .= 0
164+ %+ mod
165+ ?- -.del
166+ $increment (dec num)
167+ $decrement +(num)
168+ ==
169+ mul.qer
170+ =/ new =(0 (mod num mul.qer))
171+ ?: =(old new) ~
172+ `[%mul-of new]
173+ ==
174+```
175+
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.
177+
178+
179+## Under the hood
180+
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?
183+
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.
185+
186+
187+## Criticism
188+
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.
190+
191+Why do prize units mean what they do? Would've expected it the other way around.
192+
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.
194+
195+`++bake` should just produce `(quip move +>)`. why would we want to be forced to keep operas in state? just to avoid [~ +>] over +>?
196+
197+weirdness wrt relying on (old) state in ++feel
198+
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.)
200+
201+Lots of cruft for small applications to bother with. (Though I admit it does feel like structured cruft, at least.)