on master help sign in
Synced with current examples docs, incl expl
c674887Matthew Levan
docs/arvo/subscriptions.md+177−90
@@ -12,62 +12,124 @@ somewhat limited. A poke is a one-way message, but more often we want to
12subscribe to updates from another app. You could build a subscription model out
12subscribe to updates from another app. You could build a subscription model out
13of one-way pokes, but it's such a common pattern that it's built into arvo.
13of one-way pokes, but it's such a common pattern that it's built into arvo.
14
14
15−Let's take a look at two apps, `:examples-source` and `:examples-sink`. First,
15+Let's take a look at two apps, `:source` and `:sink`. First,
16−`:examples-source`:
16+`:source`:
17−
17+
18− /? 314
18+ :: Sends subscription updates to sink.hoon
19+ ::
20+ :::: /===/app/source/hoon
21+ ::
19 !:
22 !:
23+ ::
20 |%
24 |%
21− ++ move {bone $diff mark *}
25+ ++ move {bone card}
26+ ++ card $% {$diff diff-contents}
27+ ==
28+ ++ diff-contents $% {$noun *}
29+ ==
22 --
30 --
31+ ::
23− |_ {hid/bowl state/$~}
32+ |_ {bow/bowl $~}
33+ ::
24− ++ poke-noun
34+ ++ poke-noun
25− |= arg/*
35+ |= non/*
26 ^- {(list move) _+>.$}
36 ^- {(list move) _+>.$}
27 :_ +>.$
37 :_ +>.$
28− %+ turn (prey /example-path hid)
38+ %+ turn (prey /example-path bow)
29− |=({o/bone *} `move`[o %diff %noun arg])
39+ |=({o/bone *} [o %diff %noun non])
40+ ::
30− ++ peer-example-path
41+ ++ peer-example-path
31 |= pax/path
42 |= pax/path
32 ^- {(list move) _+>.$}
43 ^- {(list move) _+>.$}
33− ~& [%subscribed-to pax=pax]
44+ ~& source+peer-notify+'Someone subscribed to you!'
45+ ~& source+[ship+src.bow path+pax]
46+ [~ +>.$]
47+ ::
48+ ++ coup
49+ |= {wir/wire err/(unit tang)}
50+ ^- {(list move) _+>.$}
51+ ?~ err
52+ ~& source+success+'Poke succeeded!'
53+ [~ +>.$]
54+ ~& source+error+'Poke failed. Error:'
55+ ~& source+error+err
56+ [~ +>.$]
57+ ::
58+ ++ reap
59+ |= {wir/wire err/(unit tang)}
60+ ^- {(list move) _+>.$}
61+ ?~ err
62+ ~& source+success+'Peer succeeded!'
63+ [~ +>.$]
64+ ~& source+error+'Peer failed. Error:'
65+ ~& source+error+err
34 [~ +>.$]
66 [~ +>.$]
67+ ::
35 --
68 --
36
69
37−And secondly, `:examples-sink`:
70+And secondly, `:sink`:
38−
71+
39− /? 314
72+ :: Sets up a simple subscription to source.hoon
73+ ::
74+ :::: /===/app/sink/hoon
75+ ::
76+ !:
40 |%
77 |%
41− ++ move {bone card}
78+ ++ move {bone card}
42− ++ card
43− $% {$peer wire {@p term} path}
79+ ++ card $% {$peer wire dock path}
44− {$pull wire {@p term} $~}
80+ {$pull wire dock $~}
45− ==
81+ ==
46 --
82 --
47− !:
83+ ::
48− |_ {bowl available/?}
84+ |_ {bow/bowl val/?}
85+ ::
49− ++ poke-noun
86+ ++ poke-noun
50− |= arg/*
87+ |= non/*
51 ^- {(list move) _+>.$}
88 ^- {(list move) _+>.$}
52− ?: &(=(%on arg) available)
89+ ?: &(=(%on non) val)
53− [[[ost %peer /subscribe [our %source] /example-path] ~] +>.$(available |)]
90+ :_ +>.$(val |)
54− ?: &(=(%off arg) !available)
91+ :~ :* ost.bow
92+ %peer
93+ /subscribe
94+ [our.bow %source]
95+ /example-path
96+ ==
97+ ==
98+ ?: &(=(%off non) !val)
99+ :_ +>.$(val &)
55− [[[ost %pull /subscribe [our %source] ~] ~] +>.$(available &)]
100+ ~[[ost.bow %pull /subscribe [our.bow %source] ~]]
101+ ~& ?: val
102+ sink+unsubscribed+'You are now unsubscribed!'
56− ~& ?:(available %not-subscribed %subscribed)
103+ sink+subscribed+'You are now subscribed!'
57 [~ +>.$]
104 [~ +>.$]
105+ ::
58− ++ diff-noun
106+ ++ diff-noun
59− |= {wir/wire arg/*}
107+ |= {wir/wire non/*}
60 ^- {(list move) _+>.$}
108 ^- {(list move) _+>.$}
61− ~& [%received-data arg]
109+ ~& sink+received-data+'You got something!'
110+ ~& sink+data+non
62 [~ +>.$]
111 [~ +>.$]
63− ++ reap
112+ ::
113+ ++ coup
64− |= {wir/wire error/(unit tang)}
114+ |= {wir/wire err/(unit tang)}
65 ^- {(list move) _+>.$}
115 ^- {(list move) _+>.$}
66− ?~ error
116+ ?~ err
67− ~& %successfully-subscribed
117+ ~& sink+success+'Poke succeeded!'
68− [~ +>.$]
118+ [~ +>.$]
69− ~& [%subscription-failed error]
119+ ~& sink+error+'Poke failed. Error:'
120+ ~& sink+error+err
70 [~ +>.$]
121 [~ +>.$]
122+ ::
123+ ++ reap
124+ |= {wir/wire err/(unit tang)}
125+ ^- {(list move) _+>.$}
126+ ?~ err
127+ ~& sink+success+'Peer succeeded!'
128+ [~ +>.$]
129+ ~& sink+error+'Peer f ailed. Error:'
130+ ~& sink+error+err
131+ [~ +>.$]
132+ ::
71 --
133 --
72
134
73Cheat sheet:
135Cheat sheet:
@@ -116,38 +178,42 @@ Cheat sheet:
116
178
117Here's some sample output of the two working together:
179Here's some sample output of the two working together:
118
180
119− ~fintud-macrep:dojo> |start %examples-source
181+ ~fintud-macrep:dojo> |start %source
120 >=
182 >=
121− ~fintud-macrep:dojo> |start %examples-sink
183+ ~fintud-macrep:dojo> |start %sink
122 >=
184 >=
123− ~fintud-macrep:dojo> :examples-sink %on
185+ ~fintud-macrep:dojo> :sink %on
124− [%subscribed-to pax=/example-path]
186+ [%source %peer-notify 'Someone subscribed to you!']
125− %successfully-subscribed]
187+ [%source [%ship ~fintud-macrep] %path /]
188+ [%sink %success 'Peer succeeded!']
126 >=
189 >=
127− ~fintud-macrep:dojo> :examples-source 5
190+ ~fintud-macrep:dojo> :source 5
128− [%received-data 5]
191+ [%sink %received-data 'You got something!']
192+ [%sink %data 5]
129 >=
193 >=
130− ~fintud-macrep:dojo> :examples-sink %off
194+ ~fintud-macrep:dojo> :sink %off
131 >=
195 >=
132− ~fintud-macrep:dojo> :examples-source 6
196+ ~fintud-macrep:dojo> :source 6
133 >=
197 >=
134− ~fintud-macrep:dojo> :examples-sink %on
198+ ~fintud-macrep:dojo> :sink %on
135− [%subscribed-to pax=/example-path]
199+ [%source %peer-notify 'Someone subscribed to you!']
136− %successfully-subscribed]
200+ [%source [%ship ~fintud-macrep] %path /]
201+ [%sink %success 'Peer succeeded!']
137 >=
202 >=
138− ~fintud-macrep:dojo> :examples-source 7
203+ ~fintud-macrep:dojo> :source 7
139− [%received-data 7]
204+ [%sink %received-data 'You got something!']
205+ [%sink %data 7]
140 >=
206 >=
141
207
142−### :examples-source
208+### :source
143
209
144Hopefully you can get a sense for what's happening here. When we poke
210Hopefully you can get a sense for what's happening here. When we poke
145−`:examples-sink` with `%on`, `:examples-sink` subscribes to `:examples-source`,
211+`:sink` with `%on`, `:sink` subscribes to `:source`,
146−and so whenever we poke `:examples-source`, `:examples-sink` gets the update and
212+and so whenever we poke `:source`, `:sink` gets the update and
147−prints it out. Then we unsubscribe by poking `:examples-sink` with `%off`, and
213+prints it out. Then we unsubscribe by poking `:sink` with `%off`, and
148−`:examples-sink` stops getting updates. We then resubscribe.
214+`:sink` stops getting updates. We then resubscribe.
149
215
150−There's a fair bit going on in this code. Let's look at `:examples-source`
216+There's a fair bit going on in this code. Let's look at `:source`
151first.
217first.
152
218
153Our definition of `move` is fairly specific, since we're only going to sending
219Our definition of `move` is fairly specific, since we're only going to sending
@@ -161,10 +227,10 @@ discussion of ducts, a layer gets popped off the duct rather than added to it.
161This is why no wire is needed for the move -- we won't receive anything in
227This is why no wire is needed for the move -- we won't receive anything in
162response to it.
228response to it.
163
229
164−Anyways, there're two functions inside the `|_`. We already know when
230+Anyways, there are four functions (arms) inside the `|_`. We already know when
165−`++poke-noun` is called. `++peer` is called when someone tries to subscribe to
231+`++poke-noun` is called. `++peer-example-path` is called when someone tries to
166−our app. Of course, you don't just subscribe to an app; you subscribe to a path
232+subscribe to our app. Of course, you don't just subscribe to an app; you
167−on that app. This path comes in as the argument to `++peer`.
233+subscribe to a path on that app. This path comes in as the argument to `++peer`.
168
234
169In our case, we don't care what path you subscribed on, and all we do is print
235In our case, we don't care what path you subscribed on, and all we do is print
170out that you subscribed. Arvo keeps track of your subscriptions, so you don't
236out that you subscribed. Arvo keeps track of your subscriptions, so you don't
@@ -181,27 +247,27 @@ unchanged.
181
247
182Our list of moves is the result of a call to `++turn`. `++turn` is what many
248Our list of moves is the result of a call to `++turn`. `++turn` is what many
183languages call "map" -- it runs a function on every item in a list and collects
249languages call "map" -- it runs a function on every item in a list and collects
184−the results in a list. The list is `(prey /example-path hid)` and the function
250+the results in a list. The list is `(prey /example-path bow)` and the function
185is the `|=` line right after it.
251is the `|=` line right after it.
186
252
187−`++prey` is a standard library function defined in `zuse`. It takes a path and a
253+`++prey` is a standard library function defined in `zuse.hoon`. It takes a path
188−bowl and gives you a list of the subscribers who are subscribed on a path that
254+and a bowl and gives you a list of the subscribers who are subscribed on a path
189−begins with the given path. "Prey" is short for "prefix".
255+that begins with the given path. "Prey" is short for "prefix".
190
256
191Now we have the list of relevant subscribers. This a list of triples,
257Now we have the list of relevant subscribers. This a list of triples,
192`{bone @p path}`, where the only thing we really need is the bone, because we
258`{bone @p path}`, where the only thing we really need is the bone, because we
193don't need to know their urbit or what exact path they subscribed on. Thus, our
259don't need to know their urbit or what exact path they subscribed on. Thus, our
194−transformer function takes `{o/bone *}` and produces `[o %diff %noun arg]`,
260+transformer function takes `{o/bone *}` and produces `[o %diff %noun non]`,
195which is a move that provides bone `o` with this subscription update:
261which is a move that provides bone `o` with this subscription update:
196−`[%noun arg]`". This is fairly dense code, but what it's doing is
262+`[%noun non]`". This is fairly dense code, but what it's doing is
197−straightforward.
263+straightforward!
198
264
199−### :examples-sink
265+### :sink
200
266
201−`:examples-source` should now make sense. `:examples-sink` is a little longer,
267+`:source` should now make sense. `:sink` is a little longer, but not much more
202−but not much more complicated.
268+complicated.
203
269
204−In `:examples-sink`, our definition of of `++move` is different. All moves start
270+In `:sink`, our definition of of `++move` is different. All moves start
205with a `bone`, and we conventionally refer to the second half as the "card", so
271with a `bone`, and we conventionally refer to the second half as the "card", so
206that we can say a move is an action that sends a card along a bone.
272that we can say a move is an action that sends a card along a bone.
207
273
@@ -214,35 +280,56 @@ because its semantics are to cancel any subscriptions coming over this duct. If
214your bone and wire are the same as when you subscribed, then the cancellation
280your bone and wire are the same as when you subscribed, then the cancellation
215will happen correctly.
281will happen correctly.
216
282
217−The only state we need for `:examples-sink` is a boolean to indicate whether
283+The only state we need for `:sink` is a boolean to indicate whether
218−we're already subscribed to `:examples-source`. We use `available/?`, where `?`
284+we're already subscribed to `:source`. We use `val/?`, where `?`
219is the sign of type boolean (similar to `*`, `@`), which defaults to true (that
285is the sign of type boolean (similar to `*`, `@`), which defaults to true (that
220is, `0`).
286is, `0`).
221
287
222In `++poke-noun` we check our input to see both if it's `%on` and we're
288In `++poke-noun` we check our input to see both if it's `%on` and we're
223−available. If so, we produce the move to subscribe to `:examples-source`:
289+available (`val` is true). If so, we produce the move to subscribe to
224−
290+`:source`:
291+
292+ :~ :* ost.bow
293+ %peer
294+ /subscribe
295+ [our.bow %source]
225− [ost %peer /subscribe [our %source] /example-path]
296+ /example-path
226−
297+ ==
298+ ==
299+
227−Also, we set available to false (`|`) with `+>.$(available |)`.
300+Also, in the preceding lines, we set `val` to false (`|`) with `+>.$(val |)`.
228−
301+Remember that the `:_` constructs an inverted cell, with the first child
302+(`+>.$(val |` in our case) as the tail and the second child as the head. Here,
303+the cell we produce when our subscription is `%on` and `val` is true has a
304+head with our new state where `val` is set to false and a tail of our list of
305+moves, which is shown in the code block above.
306+
229−Otherwise, if our input is `%off` and we're already subscribed (i.e. `available`
307+Otherwise, if our input is `%off` and we're already subscribed (i.e. `val`
230−is false), then we unsubscribe from `:examples-source`:
308+is false), then we unsubscribe from `:source` and set `val` back to true (`&`),
231−
309+again using our handy inverted cell constructor mold `:_`:
310+
311+ :_ +>.$(val &)
232− [ost %pull /subscribe [our %source] ~]
312+ ~[[ost.bow %pull /subscribe [our.bow %source] ~]]
233
313
234It's important to send over the same bone and wire (`/subscribe`) as the one we
314It's important to send over the same bone and wire (`/subscribe`) as the one we
235originally subscribed on.
315originally subscribed on.
236
316
237−We also set `available` to true (`&`).
238−
239If neither of these cases are true, then we print our current subscription
317If neither of these cases are true, then we print our current subscription
240−state.
318+state, based on whether `val` is true or false, and return a cell containing
319+a null list of moves and our unchanged app state:
320+
321+ ~& ?: val
322+ sink+unsubscribed+'You are now unsubscribed!'
323+ sink+subscribed+'You are now subscribed!'
324+ [~ +>.$]
241
325
242`++diff-noun` is called when we get a `%diff` update along a subscription with a
326`++diff-noun` is called when we get a `%diff` update along a subscription with a
243mark of `noun`. `++diff-noun` is given the wire that we originally passed with
327mark of `noun`. `++diff-noun` is given the wire that we originally passed with
244the `%peer` subscription request along and the data we got back. In our case we
328the `%peer` subscription request along and the data we got back. In our case we
245−just print out the data.
329+just print out the data:
330+
331+ ~& sink+received-data+'You got something!'
332+ ~& sink+data+non
246
333
247`++reap` is called when we receive an acknowledgment as to whether the
334`++reap` is called when we receive an acknowledgment as to whether the
248subscription was handled successfully. You can remember that `++reap` is the
335subscription was handled successfully. You can remember that `++reap` is the
@@ -252,5 +339,5 @@ counterpart to `++peer` as it's pronounced like 'peer' backwards. Similarly,
252Moving forward, `++reap` is given the wire we attempted to subscribe over,
339Moving forward, `++reap` is given the wire we attempted to subscribe over,
253possibly along with an error message in cases of failure. `(unit type)` means
340possibly along with an error message in cases of failure. `(unit type)` means
254"either `~` or `[~ type]`, which means it's used like Haskell's "maybe" or C's
341"either `~` or `[~ type]`, which means it's used like Haskell's "maybe" or C's
255−nullability. If `error` is `~`, then the subscription was successful and we tell
342+nullability. If `err` is `~`, then the subscription was successful and we tell
256that to the user. Otherwise, we print out the error message.
343that to the user. Otherwise, we print out the error message.