@@ -5,364 +5,339 @@ sort: 3
55title: Network messages
66---
77
8−# Network messages
9−
10−Now we've learned enough hoon to do more interesting things.
11−Let's get our planets to talk to each other.
12−
13−All we've written up until now are just shell
14−commands that produce a value and then disappear. To listen for and receive messages from other planets, we'll need an app. Let's look at a very simple one:
15−
16−```
17−:: There is no love that is not an echo
18−::
19−:::: /hoon/echo/examples/app
20− ::
21−/? 314
22−!:
23−|_ {bowl state/$~}
24−++ poke-noun
25− |= arg/*
26− ^- {(list) _+>.$}
27− ~& [%argument arg]
28− [~ +>.$]
29−--
30−```
31−
32−
33−This is a very simple app that does only one thing. If you poke
34−it with a value, it prints that value out. To try this out, you
35−have to start the app, then you can poke it from the command line
36−with the following commands:
37−
38−```
39−~fintud-macrep:dojo> |start %echo
40−>=
41−~fintud-macrep:dojo> :examples-echo 5
42−[%argument 5]
43−>=
44−~fintud-macrep:dojo> :examples-echo [1 2]
45−[%argument [1 2]]
46−>=
47−```
48−
49−> There is currently a bug where the `%argument` lines are
50−> printed *above* the line you entered, so your output may not
51−> look exactly like this.
52−> `>=` means that a command was successfully received and
8+Network messages
9+================
10+
11+Now we've learned enough Hoon to do more interesting things. Let's get
12+our planets to talk to each other.
13+
14+All we've written up until now are just shell commands that produce a
15+value and then disappear. To listen for and receive messages from other
16+planets, we'll need an app. Let's look at a very simple one:
17+
18+ :: There is no love that is not an echo
19+ ::
20+ :::: /hoon/echo/examples/app
21+ ::
22+ /? 314
23+ !:
24+ |_ {bowl state/$~}
25+ ++ poke-noun
26+ |= arg/*
27+ ^- {(list) _+>.$}
28+ ~& [%argument arg]
29+ [~ +>.$]
30+ --
31+
32+This is a very simple app that does only one thing. If you poke it with
33+a value, it prints that value out. To try this out, you have to start
34+the app, then you can poke it from the command line with the following
35+commands:
36+
37+ ~fintud-macrep:dojo> |start %echo
38+ >=
39+ ~fintud-macrep:dojo> :examples-echo 5
40+ [%argument 5]
41+ >=
42+ ~fintud-macrep:dojo> :examples-echo [1 2]
43+ [%argument [1 2]]
44+ >=
45+
46+> There is currently a bug where the `%argument` lines are printed
47+> *above* the line you entered, so your output may not look exactly like
48+> this. `>=` means that a command was successfully received and
5349> executed.
5450
55−Most of the app code should be simple enough to guess its
56−function. The important part of this code is the definition of
57−`++poke-noun`.
51+Most of the app code should be simple enough to guess its function. The
52+important part of this code is the definition of `++poke-noun`.
5853
5954Once an app starts, it's always running in the background and you
60−interact with it by sending it messages. The most
61−straightforward way to do that is to poke it from the command
62−line, which we we did with `:echo 5` (`:[app-name]
63−[argument(s)]`).
64−
65−In this case, `++poke-noun` takes an argument `arg` and prints it
66−out with `~&` (sigpam). This is an unusual rune that formally "does
67−nothing", but the interpreter detects it and printfs the first
68−child, before executing the second as if the first didn't exist.
69−This is a slightly hacky way of printing to the console, but
70−we'll get to the correct way later on.
71−
72−But what does `++poke-noun` produce? Recall that `^-` casts to a
73−type. In this case, it's declaring that end result of the
74−function will be of type `{(list) _+>.$}`. But what does this
75−mean?
76−
77−The phrase to remember is "a list of moves and our state". Urbit
78−is a message passing system, so whenever we want to do something that
79−interacts with the rest of the system, we send a message. Thus, a
80−move is arvo's equivalent of a syscall. The first thing that
81−`++poke-noun` produces is a list of messages, called
82−"moves". In this case, we don't actually want the system to do
83−anything, so we produce the empty list, `~` (in the `[~ +>.$]`
84−line).
85−
86−The second thing `++poke-noun` produces is our state. `+>.$`
87−refers to a particular address in our subject where our formal
88−app state is stored. It'll become clear why this is later on,
89−but for now pretend that `+>.$` is a magic invocation that means
90−"app state".
91−
92−Let's look at another example (edit your code in `/app/examples/echo/hoon` to reflect the code below). Say we want to only accept a
93−number, and then print out the square of that number.
94−
95−```
96−/? 314
97−!:
98−|_ {bowl state=~}
99−::
100−++ poke-atom
101− |= arg/@
102− ^- {(list) _+>.$}
103− ~& [%square (mul arg arg)]
104− [~ +>.$]
105−--
106−```
107−
108−A few things have changed. Firstly, we no longer accept
109−arbitrary nouns because we can only square atoms (integers, in
110−this case an unsigned one). Thus, our argument is now `arg/@`.
111−Secondly, it's `++poke-atom` rather than `++poke-noun`.
112−
113−# Intro to marks
114−
115−Are there other `++poke`s? Yes. In fact, `noun` and
116−`atom` are just two of arbitrarily many "marks". A mark is
117−fundamentally a type definition, but accessible at the arvo
118−level. Each mark is defined in the `/mar` directory. Some marks
119−have conversion routines to other marks, and some have diff,
120−patch, and merge algorithms. None of these are required for a
55+interact with it by sending it messages. The most straightforward way to
56+do that is to poke it from the command line, which we we did with
57+`:echo 5` (`:[app-name] [argument(s)]`).
58+
59+In this case, `++poke-noun` takes an argument `arg` and prints it out
60+with `~&` ([sigpam](/hoon/twig/sig-hint/pam-dump/)). This is an unusual
61+rune that formally "does nothing", but the interpreter detects it and
62+printfs the first child, before executing the second as if the first
63+didn't exist. This is a slightly hacky way of printing to the console,
64+but we'll get to the correct way later on.
65+
66+But what does `++poke-noun` produce? Recall that `^-` casts to a type.
67+In this case, it's declaring that end result of the function will be of
68+type `{(list) _+>.$}`. But what does this mean?
69+
70+The phrase to remember is "a list of moves and our state". Urbit is a
71+message passing system, so whenever we want to do something that
72+interacts with the rest of the system, we send a message. Thus, a move
73+is arvo's equivalent of a syscall. The first thing that `++poke-noun`
74+produces is a list of messages, called "moves". In this case, we don't
75+actually want the system to do anything, so we produce the empty list,
76+`~` (in the `[~ +>.$]` line).
77+
78+The second thing `++poke-noun` produces is our state. `+>.$` refers to a
79+particular address in our subject where our formal app state is stored.
80+It'll become clear why this is later on, but for now pretend that `+>.$`
81+is a magic invocation that means "app state".
82+
83+Let's look at another example (edit your code in
84+`/app/examples/echo/hoon` to reflect the code below). Say we want to
85+only accept a number, and then print out the square of that number.
86+
87+ /? 314
88+ !:
89+ |_ {bowl state=~}
90+ ::
91+ ++ poke-atom
92+ |= arg/@
93+ ^- {(list) _+>.$}
94+ ~& [%square (mul arg arg)]
95+ [~ +>.$]
96+ --
97+
98+A few things have changed. Firstly, we no longer accept arbitrary nouns
99+because we can only square atoms (integers, in this case an unsigned
100+one). Thus, our argument is now `arg/@`. Secondly, it's `++poke-atom`
101+rather than `++poke-noun`.
102+
103+Intro to marks
104+==============
105+
106+Are there other `++poke`s? Yes. In fact, `noun` and `atom` are just two
107+of arbitrarily many "marks". A mark is fundamentally a type definition,
108+but accessible at the arvo level. Each mark is defined in the `/mar`
109+directory. Some marks have conversion routines to other marks, and some
110+have diff, patch, and merge algorithms. None of these are required for a
121111mark to exist, however.
122112
123−`noun` and `atom` are two predefined marks. In your `/mar`
124−directory, there are already many more, and you may add more
125−at will. The type associated with `noun` is `*`, and the type
126−associated with `atom` is `@`.
113+`noun` and `atom` are two predefined marks. In your `/mar` directory,
114+there are already many more, and you may add more at will. The type
115+associated with `noun` is `*`, and the type associated with `atom` is
116+`@`.
127117
128−When we poke an app from anywhere, we do so with a mark that
129−searches for the corresponding (`++poke-[mark]`) arm. Data
130−constructed on the command line is by default marked with `noun`.
131−In this case, the app is expecting an atom, so we have to
132−explicitly mark the data with `atom` using `&[mark]`. Try the following
133−commands:
118+When we poke an app from anywhere, we do so with a mark that searches
119+for the corresponding (`++poke-[mark]`) arm. Data constructed on the
120+command line is by default marked with `noun`. In this case, the app is
121+expecting an atom, so we have to explicitly mark the data with `atom`
122+using `&[mark]`. Try the following commands:
134123
135−```
136−~fintud-macrep:dojo> |start %square
137−>=
138−~fintud-macrep:dojo> :examples-square 6
139−gall: %square: no poke arm for noun
140−~fintud-macrep:dojo> :examples-square &atom 6
141−[%square 36]
142−>=
143−```
124+ ~fintud-macrep:dojo> |start %square
125+ >=
126+ ~fintud-macrep:dojo> :examples-square 6
127+ gall: %square: no poke arm for noun
128+ ~fintud-macrep:dojo> :examples-square &atom 6
129+ [%square 36]
130+ >=
144131
145−> Recall the bug where `%square` may get printed above the input
146−> line.
132+> Recall the bug where `%square` may get printed above the input line.
147133
148−Marks are powerful, and they're the backbone of Urbit's data
149−pipeline, so we'll be getting quite used to them.
134+Marks are powerful, and they're the backbone of Urbit's data pipeline,
135+so we'll be getting quite used to them.
150136
151137**Exercise**:
152138
153−- Write an app that computes fizzbuzz on its input (as in the
154− previous section).
155−
156−# Sending a message to another urbit
157−
158−Let's write our first network message! Here's `/app/examples/pong.hoon`:
159−
160−```
161−/? 314
162−|%
163− ++ move {bone term wire *}
164−--
165−!:
166−|_ {bowl state=$~}
167−::
168−++ poke-urbit
169− |= to/@p
170− ^- [(list move) _+>.$]
171− [[[ost %poke /sending [to %pong] %atom 'howdy'] ~] +>.$]
172−::
173−++ poke-atom
174− |= arg/@
175− ^- (list move) _+>.$}
176− ~& [%receiving (@t arg)]
177− [~ +>.$]
178−::
179−++ coup |=(* [~ +>.$])
180−--
181−```
139+- Write an app that computes fizzbuzz on its input (as in the previous
140+ section).
141+
142+Sending a message to another urbit
143+==================================
144+
145+Let's write our first network message! Here's `/app/examples/pong.hoon`:
146+
147+ /? 314
148+ |%
149+ ++ move {bone term wire *}
150+ --
151+ !:
152+ |_ {bowl state=$~}
153+ ::
154+ ++ poke-urbit
155+ |= to/@p
156+ ^- [(list move) _+>.$]
157+ [[[ost %poke /sending [to %pong] %atom 'howdy'] ~] +>.$]
158+ ::
159+ ++ poke-atom
160+ |= arg/@
161+ ^- (list move) _+>.$}
162+ ~& [%receiving (@t arg)]
163+ [~ +>.$]
164+ ::
165+ ++ coup |=(* [~ +>.$])
166+ --
182167
183168Run it with these commands:
184169
185−```
186−~fintud-macrep:dojo> |start %examples-pong
187−>=
188−~fintud-macrep:dojo> :pong &examples-urbit ~sampel-sipnym
189−>=
190−```
170+ ~fintud-macrep:dojo> |start %examples-pong
171+ >=
172+ ~fintud-macrep:dojo> :pong &examples-urbit ~sampel-sipnym
173+ >=
191174
192−Replace `~sampel-sipnym` with another urbit. The easiest thing to
193−do is to start a moon, a sub-identity of your urbit. If you don't know how to start a moon, see [the user admin section](/docs/using/admin/). Don't
194−forget to start the `%examples-pong` app on that urbit, too. You should
175+Replace `~sampel-sipnym` with another urbit. The easiest thing to do is
176+to start a moon, a sub-identity of your urbit. If you don't know how to
177+start a moon, see [the user admin section](/docs/using/admin/). Don't
178+forget to start the `%examples-pong` app on that urbit, too. You should
195179see, on the foreign urbit, this output:
196180
197−```
198−[%receiving 'howdy']
199−```
181+ [%receiving 'howdy']
200182
201−Most of the code should be straightforward. In `++poke-atom`,
202−the only new thing is the expression `(@t arg)`, which is the type `@t` being called as a function with argument `arg`. As we already
203−know, `@t` is the type of "cord" text strings. In Hoon, when types are called as functions, they serve as a validator function called a "clam" -- that is, a function whose domain is all nouns, and range is the given type (in this case, `@t`).
183+Most of the code should be straightforward. In `++poke-atom`, the only
184+new thing is the expression `(@t arg)`, which is the type `@t` being
185+called as a function with argument `arg`. As we already know, `@t` is
186+the type of "cord" text strings. In Hoon, when types are called as
187+functions, they serve as a validator function called a "clam" -- that
188+is, a function whose domain is all nouns, and range is the given type
189+(in this case, `@t`).
204190
205−In simpler terms, if a clam is passed a value of its own
206−type, it produces that value. Otherwise, it
207−produces the default value (aka the "bunt") of its type. Here we call
208−this `@t` function on the argument. This coerces the argument
209−to text, so that we can print it out prettily no matter what
210−we're passed.
191+In simpler terms, if a clam is passed a value of its own type, it
192+produces that value. Otherwise, it produces the default value (aka the
193+"bunt") of its type. Here we call this `@t` function on the argument.
194+This coerces the argument to text, so that we can print it out prettily
195+no matter what we're passed.
211196
212−The more interesting part is in `++poke-urbit`. The `urbit` mark
213−is an urbit identity, and the hoon type associated with it is
214−`@p` (the "p" stands for "phonetic base").
197+The more interesting part is in `++poke-urbit`. The `urbit` mark is an
198+urbit identity, and the Hoon type associated with it is `@p` (the "p"
199+stands for "phonetic base").
215200
216201Recall that in a `++poke` arm we produce "a list of moves and our
217−state". Until now, we've left the list of moves empty, since we
218−haven't wanted to tell arvo to do anything in particular. Now we
219−want to send a message to another urbit. Thus, we produce a list
220−with one element:
202+state". Until now, we've left the list of moves empty, since we haven't
203+wanted to tell arvo to do anything in particular. Now we want to send a
204+message to another urbit. Thus, we produce a list with one element:
221205
222−```
223−[ost %poke /sending [to-urbit-address %pong] %atom 'howdy']
224−```
206+ [ost %poke /sending [to-urbit-address %pong] %atom 'howdy']
225207
226208### Moves
227209
228−The general form of a move is
229−
230−-arvo part---->
231−`[bone term wire *]`
232−
233−------------------------->app/vane-specific part of move
210+The general form of a move is `[bone term wire *]`.
234211
235212Or, in pseudo-code:
236213
237−`["cause" sys-call/action tack-new-layer-on-cause action-specific
238−information]`
214+`["cause" sys-call/action tack-new-layer-on-cause action-specific information]`
239215
240216Let's walk through each of these elements step by step.
241217
242218#### Bones ("cause")
243219
244−If you look up `++bone` in `hoon.hoon`, you'll see that it's a
245−number (`@ud`), and that it's an opaque reference to a duct.
246−`++duct` in hoon.hoon is a list of `wire`s, where `++wire` is an
247−alias for `++path`. `++path` is a list of `++span`s, which are ASCII
248−text. Thus, a duct is a list of paths, and a bone is an opaque
249−reference to it (in the same way that a Unix file
250−descriptor is an opaque reference to a file structure). Thus, to
251−truly understand bones, we must understand ducts.
252−
253−A duct is stack of causes, again, represented as paths, which are
254−called wires. At the bottom of every duct is a unix event, such
255−as a keystroke, network packet, file change, or timer event.
256−When arvo is given this event, it routes the event to appropriate
257−kernel module for handling.
258−
259−Sometimes, the module can immediately handle the event and
260−produce any necessary results. For example, when we poked the
261−`++poke-atom` arm above, we poked %gall, our application server,
262−which was able to respond to our poke directly. When the module
263−cannot service the request itself, it sends instructions to another kernel
264−module or application (the the %gall module) to do a specified action, and produces the result from that.
265−
266−Furthermore, when one module sends a message to another kernel module or application, it also sends along the duct it was given
267−with its new wire tacked onto the end. Now the duct has two entries,
268−with the unix even on the bottom and the kernel module that
269−handled it next. This process can continue indefinitely, adding
270−more and more layers onto the duct. When an entity finally
271−produces a result, a layer is popped off the duct, and the result
272−is passed all the way back down.
273−
274−In effect, a duct is an arvo-level call stack. It's worth noting
275−that while in traditional call stacks a function call happens
276−synchronously and returns exactly once. In arvo, multiple moves
277−can be sent at once, are evaluated asynchronously, and each
278−one may be responded to zero or more times.
220+If you look up `++bone` in `hoon.hoon`, you'll see that it's a number
221+(`@ud`), and that it's an opaque reference to a duct. `++duct` in
222+hoon.hoon is a list of `wire`s, where `++wire` is an alias for `++path`.
223+`++path` is a list of `++span`s, which are ASCII text. Thus, a duct is a
224+list of paths, and a bone is an opaque reference to it (in the same way
225+that a Unix file descriptor is an opaque reference to a file structure).
226+Thus, to truly understand bones, we must understand ducts.
227+
228+A duct is stack of causes, again, represented as paths, which are called
229+wires. At the bottom of every duct is a unix event, such as a keystroke,
230+network packet, file change, or timer event. When arvo is given this
231+event, it routes the event to appropriate kernel module for handling.
232+
233+Sometimes, the module can immediately handle the event and produce any
234+necessary results. For example, when we poked the `++poke-atom` arm
235+above, we poked %gall, our application server, which was able to respond
236+to our poke directly. When the module cannot service the request itself,
237+it sends instructions to another kernel module or application (the the
238+%gall module) to do a specified action, and produces the result from
239+that.
240+
241+Furthermore, when one module sends a message to another kernel module or
242+application, it also sends along the duct it was given with its new wire
243+tacked onto the end. Now the duct has two entries, with the unix even on
244+the bottom and the kernel module that handled it next. This process can
245+continue indefinitely, adding more and more layers onto the duct. When
246+an entity finally produces a result, a layer is popped off the duct, and
247+the result is passed all the way back down.
248+
249+In effect, a duct is an arvo-level call stack. It's worth noting that
250+while in traditional call stacks a function call happens synchronously
251+and returns exactly once. In arvo, multiple moves can be sent at once,
252+are evaluated asynchronously, and each one may be responded to zero or
253+more times.
279254
280255The point to take home is that whatever caused `++poke-urbit` to be
281−called is also the root cause for the network message we're
282−trying to send. Thus, we say to send the network message along the given bone
256+called is also the root cause for the network message we're trying to
257+send. Thus, we say to send the network message along the given bone
283258`ost`.
284259
285260##### Wire ('tack on new layer to duct')
286261
287−Of course, we have to push a new layer onto our duct
288−before passing it along (or responding to it directly) anywhere. This
289−layer can have any data we want in it, but we don't need anything
290−specific here, so we just use the wire `/sending`
291−(`/elem1/elem2/elemN` is one syntax used to create `++path`s and
292−`++wire`s of N elements). If we were expecting a response (which
293−we're not), it would come back along the `/sending` wire, meaning
294−that the path `/sending` will be passed back to the response
295−handler as an argument. Although it's not required, it's
296−generally a good idea to make the wire human-readable for
297−bug-handling purposes.
262+Of course, we have to push a new layer onto our duct before passing it
263+along (or responding to it directly) anywhere. This layer can have any
264+data we want in it, but we don't need anything specific here, so we just
265+use the wire `/sending` (`/elem1/elem2/elemN` is one syntax used to
266+create `++path`s and `++wire`s of N elements). If we were expecting a
267+response (which we're not), it would come back along the `/sending`
268+wire, meaning that the path `/sending` will be passed back to the
269+response handler as an argument. Although it's not required, it's
270+generally a good idea to make the wire human-readable for bug-handling
271+purposes.
298272
299273##### Term (sys-call)
300274
301275Each move also has a `term`, composed of lowercase ASCII and/or `-`.
302−This `term` has the sign `@tas`. In this case, out `term` is `%poke`,
303−which is the name of the particular kind of move we're sending.
304−You can always use `%poke` to message an app. Other common names
305−include `%warp`, to read from the filesystem; `%wait`, to
306−set a timer; and `%them`, to send an http request.
276+This `term` has the sign `@tas`. In this case, out `term` is `%poke`,
277+which is the name of the particular kind of move we're sending. You can
278+always use `%poke` to message an app. Other common names include
279+`%warp`, to read from the filesystem; `%wait`, to set a timer; and
280+`%them`, to send an http request.
307281
308282The move ends with `*` (that is, any noun) since each type of move takes
309−different data. In our case, a `%poke` move takes a target
310−(urbit and app) and marked data, then pokes the arm of the
311−corresponding mark on that app on that urbit
312−with that data. `[to-urbit-address %pong]` is the target urbit and app,
313−`%atom` is the mark`, and `'howdy'` is the data.
314−
315−When arvo receives a `%poke` move, it calls the appropriate
316−`++poke`. The same mechanism is used for sending messages
317−between apps on the same urbit as for sending messages between
318−apps on different urbits.
319−
320−> We said earlier that we're not expecting a response. This is
321−> not entirely true: the `++coup` is called when we receive
322−> acknowledgment that the `++poke` was called. We don't do
323−> anything with this information right now, but we could.
283+different data. In our case, a `%poke` move takes a target (urbit and
284+app) and marked data, then pokes the arm of the corresponding mark on
285+that app on that urbit with that data. `[to-urbit-address %pong]` is the
286+target urbit and app, `%atom` is the mark`, and`'howdy'\` is the data.
287+
288+When arvo receives a `%poke` move, it calls the appropriate `++poke`.
289+The same mechanism is used for sending messages between apps on the same
290+urbit as for sending messages between apps on different urbits.
291+
292+> We said earlier that we're not expecting a response. This is not
293+> entirely true: the `++coup` is called when we receive acknowledgment
294+> that the `++poke` was called. We don't do anything with this
295+> information right now, but we could.
324296
325297**Exercises**:
326298
327−- Extend either of the apps in the first two exercises to accept
328− input over the network in the same way as `pong`.
329−
330−- Modify `examples-pong` to print out a message when it receives acknowledgement.
331−
332−- Write two apps, `even` and `odd`. When you pass an atom to
333− `even`, check whether it's even. If so, divide it by two and
334− recurse; otherwise, poke `odd` with it. When `odd` recieves
335− an atom, check whether it's equal to one. If so, terminate,
336− printing "%success". Otherwise, check whether it's odd. If
337− so, multiply it by three, add one, and recurse; otherwise, poke
338− `even` with it. When either app receives a number, print it out along with the name of the app. In the end, you should be able to watch Collatz's
339− conjecture play out between the two apps. Sample output:
340−
341−```
342−~fintud-macrep:dojo> :even &atom 18
343−[%even 18]
344−[%odd 9]
345−[%even 28]
346−[%even 14]
347−[%odd 7]
348−[%even 22]
349−[%odd 11]
350−[%even 34]
351−[%odd 17]
352−[%even 52]
353−[%even 26]
354−[%odd 13]
355−[%even 40]
356−[%even 20]
357−[%even 10]
358−[%odd 5]
359−[%even 16]
360−[%even 8]
361−[%even 4]
362−[%even 2]
363−%success
364−```
365−
366−- Put `even` and `odd` on two separate urbits and pass the
367− messages over the network. Post a link to a working solution
368− in :talk to receive a cookie.
299+- Extend either of the apps in the first two exercises to accept input
300+ over the network in the same way as `pong`.
301+
302+- Modify `examples-pong` to print out a message when it receives
303+ acknowledgement.
304+
305+- Write two apps, `even` and `odd`. When you pass an atom to `even`,
306+ check whether it's even. If so, divide it by two and recurse;
307+ otherwise, poke `odd` with it. When `odd` recieves an atom, check
308+ whether it's equal to one. If so, terminate, printing "%success".
309+ Otherwise, check whether it's odd. If so, multiply it by three, add
310+ one, and recurse; otherwise, poke `even` with it. When either app
311+ receives a number, print it out along with the name of the app. In
312+ the end, you should be able to watch Collatz's conjecture play out
313+ between the two apps. Sample output:
314+
315+<!-- -->
316+
317+ ~fintud-macrep:dojo> :even &atom 18
318+ [%even 18]
319+ [%odd 9]
320+ [%even 28]
321+ [%even 14]
322+ [%odd 7]
323+ [%even 22]
324+ [%odd 11]
325+ [%even 34]
326+ [%odd 17]
327+ [%even 52]
328+ [%even 26]
329+ [%odd 13]
330+ [%even 40]
331+ [%even 20]
332+ [%even 10]
333+ [%odd 5]
334+ [%even 16]
335+ [%even 8]
336+ [%even 4]
337+ [%even 2]
338+ %success
339+
340+- Put `even` and `odd` on two separate urbits and pass the messages
341+ over the network. Post a link to a working solution in :talk to
342+ receive a cookie.
343+