8We've dealt fairly extensively with "poke" messages to an app,
9but these are somewhat limited. A poke is a one-way message, but
10more often we want to subscribe to updates from another app. You
11could build a subscription model out of one-way pokes, but it's
12such a common pattern that it's built into arvo.
14Let's take a look at two apps, `:examples-source` and `:examples-sink`. First,
21++ move {bone $diff mark *}
26 ^- {(list move) _+>.$}
28 %+ turn (prey /the-path hid)
29 |=({o/bone *} `move`[o %diff %noun arg])
32 ^- {(list move) _+>.$}
33 ~& [%subscribed-to pax=pax]
38And secondly, `:examples-sink`:
45 $% {$peer wire {@p term} path}
46 {$pull wire {@p term} $~}
53 ^- {(list move) _+>.$}
54 ?: &(=(%on arg) available)
55 [[[ost %peer /subscribe [our %source] /the-path] ~] +>.$(available |)]
56 ?: &(=(%off arg) !available)
57 [[[ost %pull /subscribe [our %source] ~] ~] +>.$(available &)]
58 ~& ?:(available %not-subscribed %subscribed)
62 ^- {(list move) _+>.$}
63 ~& [%recieved-data arg]
66 |= {wir/wire error/(unit tang)}
67 ^- {(list move) _+>.$}
69 ~& %successfully-subscribed
71 ~& [%subscription-failed error]
79- `&` can either be the boolean true (along with `%.y`, `0`), or
80 the irregular wide form of the `$&` rune, which computes the
81 logical 'and' operation on its two children.
83- Similar to `&`,`|` is either the boolean false (along with `%.n` and `1`), or
84 the irregular short for of `?|`, which computes the logical 'or' operation on
87- `!` is the irregular wide form of `?!`, which computes the logical `not` on
90- `?~` is basically an if-then-else statement that checks whether condition `p`
91 is `~` (null). `?~` is slightly different to `?:(~ %tru %fal) in that it
92 reduces to ?:($=(%type value) %tru %false). `$=` tests whether value `q`
93 falls within type `p`. One thing to watch out for in hoon: if you do `?~`, it
94 affects the type of the conditional value: XXexample
96- `:_` is just an inverted `:-`: all it does is accept children `p` and `q` and
97 produce an inverted tuple of the two, `[q p]`.
99- `++bowl` is the type of our system state within our app. For example, it
100 includes things like `our`, the ship name of the host, and `now`, the
103- `$%` is a type constructor that defines a type composed of `n`
104 types that it is passed. For example `$% @ * ^ ==` is the
105 type of either `@`, `*`, or a cell `^`. XX this a union, right?
107- You may have noticed the separate `|%` above the application
108 core `|_`. We usually put our types in another core on top of the
109 application core. We can access these type from our `|_`
110 because in hoon.hoon files, all cores are =>'ed (called) against
111 each other. Thus the `|%` with the types is in the context of
112 the `|_`, as it lies above it: hoon.hoon `=> |% w types =>
118Here's some sample output of the two working together:
121~fintud-macrep:dojo> |start %examples-source
123~fintud-macrep:dojo> |start %examples-sink
125~fintud-macrep:dojo> :examples-sink %on
126[%subscribed-to pax=/the-path]
127%successfully-subscribed]
129~fintud-macrep:dojo> :examples-source 5
132~fintud-macrep:dojo> :examples-sink %off
134~fintud-macrep:dojo> :examples-source 6
136~fintud-macrep:dojo> :examples-sink %on
137[%subscribed-to pax=/the-path]
138%successfully-subscribed]
140~fintud-macrep:dojo> :examples-source 7
147Hopefully you can get a sense for what's happening here. When we
148poke `:examples-sink` with `%on`, `:examples-sink` subscribes to `:examples-source`, and so
149whenever we poke `:examples-source`, `:examples-sink` gets the update and prints it
150out. Then we unsubscribe by poking `:examples-sink` with `%off`, and
151`:examples-sink` stops getting updates. We then resubscribe.
153There's a fair bit going on in this code. Let's look at
154`:examples-source` first.
156Our definition of `move` is fairly specific, since we're only
157going to sending one kind of move. The `%diff` move is a
158subscription update, and its content is marked data which gall
159routes to our subscribers.
161This is a slightly different kind of move than we've dealt with
162so far. It's producing a result rather than calling other code
163(i.e. it's a return rather than a function call), so if you
164recall the discussion of ducts, a layer gets popped off the duct
165rather than added to it. This is why no wire is needed for the
166move -- we won't receive anything in response to it.
168Anyways, there're two functions inside the `|_`. We already know
169when `++poke-noun` is called. `++peer` is called when someone
170tries to subscribe to our app. Of course, you don't just
171subscribe to an app; you subscribe to a path on that app. This
172path comes in as the argument to `++peer`.
174In our case, we don't care what path you subscribed on, and all
175we do is print out that you subscribed. Arvo keeps track of your
176subscriptions, so you don't have to. You can access your
177subscribers by looking at `sup` in the bowl that's passed in.
178`sup` is of type `(map bone {@p path})`, which associates bones
179with the urbit who subscribed, and which path they subscribed on.
180If you want to communicate with your subscribers, send them messages along
183`++poke-noun` "spams" the given arguement to all our subscribers.
184There's a few things we haven't seen before. Firstly, `:_(a b)`
185is the same as `[b a]`. It's just a convenient way of formatting
186things when the first thing in a cell is much more complicated
187than the second. Thus, we're producing our state unchanged.
189Our list of moves is the result of a call to `++turn`. `++turn`
190is what many languages call "map" -- it runs a function on every
191item in a list and collects the results in a list. The list is
192`(prey /the-path hid)` and the function is the `|=` line right
195`++prey` is a standard library function defined in `zuse`. It
196takes a path and a bowl and gives you a list of the subscribers
197who are subscribed on a path that begins with the given path.
198"Prey" is short for "prefix".
200Now we have the list of relevant subscribers. This a list of
201triples, `{bone @p path}`, where the only thing we really need is
202the bone because we don't care what urbit they are or what exact
203path they subscribed on. Thus, our transformer function takes
204`{o/bone *}` and produces `[o %diff %noun arg]`, which is a move
205that provides bone `o` with this subscription update: `[%noun
206arg]`". This is fairly dense code, but what it's doing is
211`:examples-source` should now make sense. `:examples-sink` is a little longer,
212but not much more complicated.
214In `:examples-sink`, our definition of of `++move` is different. All
215moves start with a `bone`, and we conventionally refer to the
216second half as the "card", so that we can say a move is an action
217that sends a card along a bone.
219We have two kinds of cards here: we `%peer` to start a
220subscription, and we `%pull` to stop it. Both of these are
221"forward" moves that may receive a response, so they need a wire
222to tack onto the duct before they pass it on. They also need a
223target, which is a pair of an urbit and an app name. Additionally, `%peer`
224needs a path on that app to subscribe too. `%pull` doesn't need
225this, because its semantics are to cancel any subscriptions
226coming over this duct. If your bone and wire are the same as
227when you subscribed, then the cancellation will happen correctly.
229The only state we need for `:examples-sink` is a boolean to indicate whether
230we're already subscribed to `:examples-source`. We use `available/?`,
231where `?` is the sign of type boolean (similar to `*`, `@`) (which defaults to true).
233In `++poke-noun` we check our input to see both if it's `%on` and
234we're available. If so, we produce the move to subscribe to
238[ost %peer /subscribe [our %source] /the-path]
241Also, we set available to false (`|`) with `+>.$(available |)`.
243Otherwise, if our input is `%off` and we're already subscribed
244(i.e. `available` is false), then we unsubscribe from
248[ost %pull /subscribe [our %source] ~]
251It's important to send over the same bone and wire (`/subscribe`)
252as the one we originally subscribed on.
254We also set `available` to true (`&`).
256If neither of these cases are true, then we just print out our
257current subscription state and do nothing.
259`++diff-noun` is called when we get a `%diff` update along a
260subscription with a mark of `noun`. `++diff-noun` is given the
261wire that we originally passed with the `%peer` subscription
262request along and the data we got back. In our case we just
265`++reap` is called when we receive an acknowledgment as to
266whether the subscription was handled successfully. You can
267remember that `++reap` is the counterpart to `++peer` as it's
268almost spelled the same as 'peer' backwards. Similarly, `coup` is
269similar to 'poke' backwards.
271Moving forward, `++reap` is given the wire we attempted to
272subscribe over, possibly along with an error message in cases of
273failure. `(unit type)` means "either `~` or `[~ type]`, which
274means it's used like Haskell's "maybe" or C's nullability. If
275`error` is `~`, then the subscription was successful and we tell
276that to the user. Otherwise, we print out the error message.
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