@@ -5,369 +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−<blockquote class="blockquote">
50−There is currently a bug where the `%argument` lines are
51−printed *above* the line you entered, so your output may not
52−look exactly like this.
53−`>=` means that a command was successfully received and
54−executed.
55−</blockquote>
56−
57−Most of the app code should be simple enough to guess its
58−function. The important part of this code is the definition of
59−`++poke-noun`.
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
49+> executed.
50+
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`.
6053
6154Once an app starts, it's always running in the background and you
62−interact with it by sending it messages. The most
63−straightforward way to do that is to poke it from the command
64−line, which we we did with `:echo 5` (`:[app-name]
65−[argument(s)]).
66−
67−In this case, `++poke-noun` takes an argument `arg` and prints it
68−out with `~&`. This is an unusual rune that formally "does
69−nothing", but the interpreter detects it and printfs the first
70−child, before executing the second as if the first didn't exist.
71−This is a slightly hacky way of printing to the console, but
72−we'll get to the correct way later on.
73−
74−But what does `++poke-noun` produce? Recall that `^-` casts to a
75−type. In this case, it's declaring that end result of the
76−function will be of type `{(list) _+>.$}`. But what does this
77−mean?
78−
79−The phrase to remember is "a list of moves and our state". Urbit
80−is a message passing system, so whenever we want to do something that
81−interacts with the rest of the system, we send a message. Thus, a
82−move is arvo's equivalent of a syscall. The first thing that
83−`++poke-noun` produces is a list of messages, called
84−"moves". In this case, we don't actually want the system to do
85−anything, so we produce the empty list, `~` (in the `[~ +>.$]`
86−line).
87−
88−The second thing `++poke-noun` produces is our state. `+>.$`
89−refers to a particular address in our subject where our formal
90−app state is stored. It'll become clear why this is later on,
91−but for now pretend that `+>.$` is a magic invocation that means
92−"app state".
93−
94−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
95−number, and then print out the square of that number.
96−
97−```
98−/? 314
99−!:
100−|_ {bowl state=~}
101−::
102−++ poke-atom
103− |= arg/@
104− ^- {(list) _+>.$}
105− ~& [%square (mul arg arg)]
106− [~ +>.$]
107−--
108−```
109−
110−A few things have changed. Firstly, we no longer accept
111−arbitrary nouns because we can only square atoms (integers, in
112−this case an unsigned one). Thus, our argument is now `arg/@`.
113−Secondly, it's `++poke-atom` rather than `++poke-noun`.
114−
115−# Intro to marks
116−
117−Are there other `++poke`s? Yes. In fact, `noun` and
118−`atom` are just two of arbitrarily many "marks". A mark is
119−fundamentally a type definition, but accessible at the arvo
120−level. Each mark is defined in the `/mar` directory. Some marks
121−have conversion routines to other marks, and some have diff,
122−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
123111mark to exist, however.
124112
125−`noun` and `atom` are two of dozens of predefined marks (that will
126−already be in your `/mar` directory), and the
127−user may add more at will. The type associated with `noun` is
128−`*`, and the type 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+`@`.
129117
130−When we poke an app from anywhere, we do so with a mark that
131−searches for the corresponding (`++poke-[mark]`) arm. Data
132−constructed on the command line is by default marked with `noun`.
133−In this case, the app is expecting an atom, so we have to
134−explicitly mark the data with `atom` using `&[mark]`. Try the following
135−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:
136123
137−```
138−~fintud-macrep:dojo> |start %square
139−>=
140−~fintud-macrep:dojo> :examples-square 6
141−gall: %square: no poke arm for noun
142−~fintud-macrep:dojo> :examples-square &atom 6
143−[%square 36]
144−>=
145−```
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+ >=
146131
147−> Recall the bug where `%square` may get printed above the input
148−> line.
132+> Recall the bug where `%square` may get printed above the input line.
149133
150−Marks are powerful, and they're the backbone of urbit's data
151−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.
152136
153137**Exercise**:
154138
155−- Write an app that computes fizzbuzz on its input (as in the
156− previous section).
157−
158−# Sending a message to another ship
159−
160−Let's write our first network message! Here's `/app/examples/pong.hoon`:
161−
162−```
163−/? 314
164−|%
165− ++ move {bone term wire *}
166−--
167−!:
168−|_ {bowl state=$~}
169−::
170−++ poke-urbit
171− |= to/@p
172− ^- [(list move) _+>.$]
173− [[[ost %poke /sending [to %pong] %atom 'howdy'] ~] +>.$]
174−::
175−++ poke-atom
176− |= arg/@
177− ^- (list move) _+>.$}
178− ~& [%receiving (@t arg)]
179− [~ +>.$]
180−::
181−++ coup |=(* [~ +>.$])
182−--
183−```
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+ --
184167
185168Run it with these commands:
186169
187−```
188−~fintud-macrep:dojo> |start %examples-pong
189−>=
190−~fintud-macrep:dojo> :pong &examples-urbit ~sampel-sipnym
191−>=
192−```
170+ ~fintud-macrep:dojo> |start %examples-pong
171+ >=
172+ ~fintud-macrep:dojo> :pong &examples-urbit ~sampel-sipnym
173+ >=
193174
194−Replace `~sampel-sipnym` with another urbit. The easiest thing to
195−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
196−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
197179see, on the foreign urbit, this output:
198180
199−```
200−[%receiving 'howdy']
201−```
181+ [%receiving 'howdy']
202182
203−Most of the code should be straightforward. In `++poke-atom`,
204−the only new thing is the expression `(@t arg)`. As we already
205−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" in hoon) -- that is, a function whose domain is all nouns and range is the given type.
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`).
206190
207−In simpler terms, if a validator function is passed a value that
208−falls within its type, that value is produced. Otherwise, it
209−produces the default value (aka the "bunt") of its type. Here we call
210−this `@t` function on the argument. This coerces the argument
211−to text, so that we can print it out prettily no matter what
212−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.
213196
214−The more interesting part is in `++poke-urbit`. The `urbit` mark
215−is an urbit identity, and the hoon type associated with it is
216−`@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").
217200
218201Recall that in a `++poke` arm we produce "a list of moves and our
219−state". Until now, we've left the list of moves empty, since we
220−haven't wanted to tell arvo to do anything in particular. Now we
221−want to send a message to another urbit. Thus, we produce a list
222−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:
223205
224−```
225−[ost %poke /sending [to-urbit-address %pong] %atom 'howdy']
226−```
206+ [ost %poke /sending [to-urbit-address %pong] %atom 'howdy']
227207
228208### Moves
229209
230−The general form of a move is
231−
232−-arvo part---->
233−`[bone term wire *]`
234−
235−------------------------->app/vane-specific part of move
210+The general form of a move is `[bone term wire *]`.
236211
237212Or, in pseudo-code:
238213
239−`["cause" sys-call/action tack-new-layer-on-cause action-specific
240−information]`
214+`["cause" sys-call/action tack-new-layer-on-cause action-specific information]`
241215
242216Let's walk through each of these elements step by step.
243217
244218#### Bones ("cause")
245219
246−If you look up `++bone` in `hoon.hoon`, you'll see that it's a
247−number (`@ud`), and that it's an opaque reference to a duct.
248−`++duct` in hoon.hoon is a list of `wire`s, where `++wire` is an
249−alias for `++path`. `++path` is a list of `++span`s, which are ASCII
250−text. Thus, a duct is a list of paths, and a bone is an opaque
251−reference to it (in the same way that a Unix file
252−descriptor is an opaque reference to a file structure). Thus, to
253−truly understand bones, we must understand ducts.
254−
255−A duct is stack of causes, again, represented as paths, which are
256−called wires. At the bottom of every duct is a unix event, such
257−as a keystroke, network packet, file change, or timer event.
258−When arvo is given this event, it routes the event to appropriate
259−kernel module for handling.
260−
261−Sometimes, the module can immediately handle the event and
262−produce any necessary results. For example, when we poked the
263−`++poke-atom` arm above, we poked %gall, our application server,
264−which was able to respond to our poke directly. When the module
265−cannot service the request itself, it sends instructions to another kernel
266−module or application (the the %gall module) to do a specified action, and produces the result from that.
267−
268−Furthermore, when one module sends a message to another kernel module or application, it also sends along the duct it was given
269−with its new wire tacked onto the end. Now the duct has two entries,
270−with the unix even on the bottom and the kernel module that
271−handled it next. This process can continue indefinitely, adding
272−more and more layers onto the duct. When an entity finally
273−produces a result, a layer is popped off the duct, and the result
274−is passed all the way back down.
275−
276−In effect, a duct is an arvo-level call stack. It's worth noting
277−that while in traditional call stacks a function call happens
278−synchronously and returns exactly once. In arvo, multiple moves
279−can be sent at once, are evaluated asynchronously, and each
280−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.
281254
282255The point to take home is that whatever caused `++poke-urbit` to be
283−called is also the root cause for the network message we're
284−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
285258`ost`.
286259
287260##### Wire ('tack on new layer to duct')
288261
289−Of course, we have to push a new layer onto our duct
290−before passing it along (or responding to it directly) anywhere. This
291−layer can have any data we want in it, but we don't need anything
292−specific here, so we just use the wire `/sending`
293−(`/elem1/elem2/elem n` is one syntax used to create `++path`s and
294−`++wire`s of n elements). If we were expecting a response (which
295−we're not), it would come back along the `/sending` wire, meaning
296−that the path `/sending` will be passed back to the response
297−handler as an argument. Although it's not required, it's
298−generally a good idea to make the wire human-readable for
299−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.
300272
301273##### Term (sys-call)
302274
303−Looking back at the general form of a move, there is a
304−`term`--which is text composed lowercase ascii and `-`, and has
305−the sign of `@tas`-- which in this case is `%poke`. This is the
306−name of the particular kind of move we're sending. If you think
307−of a move as a syscall (which you should), then this `term` is
308−the name of the syscall. Common ones include: `%poke`, to
309−message an app; `%warp`, to read from the filesystem; `%wait`, to
310−set a timer; and `%them`, to send an http request.
311−
312−The general form ends with `*` (a noun) since each type of move takes
313−different data. In our case, a `%poke` move takes a target
314−(urbit and app) and marked data and pokes the arm of the
315−corresponding mark on that app on that urbit
316−with that data. `[to-urbit-address %pong]` is the target urbit and app,
317−`%atom` is the mark`, and `'howdy'` is the data.
318−
319−When arvo receives a `%poke` move, it calls the appropriate
320−`++poke`. The same mechanism is used for sending messages
321−between apps on the same urbit as for sending messages between
322−apps on different urbits.
323−
324−<blockquote class="blockquote">
325−We said earlier that we're not expecting a response. This is
326−not entirely true: the `++coup` is called when we receive
327−acknowledgment that the `++poke` was called. We don't do
328−anything with this information right now, but we could.
329−</blockquote>
275+Each move also has a `term`, composed of lowercase ASCII and/or `-`.
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.
281+
282+The move ends with `*` (that is, any noun) since each type of move takes
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.
330296
331297**Exercises**:
332298
333−- Extend either of the apps in the first two exercises to accept
334− input over the network in the same way as `pong`.
335−
336−- Modify `examples-pong` to print out a message when it receives acknowledgement.
337−
338−- Write two apps, `even` and `odd`. When you pass an atom to
339− `even`, check whether it's even. If so, divide it by two and
340− recurse; otherwise, poke `odd` with it. When `odd` recieves
341− an atom, check whether it's equal to one. If so, terminate,
342− printing "%success". Otherwise, check whether it's odd. If
343− so, multiply it by three, add one, and recurse; otherwise, poke
344− `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
345− conjecture play out between the two apps. Sample output:
346−
347−```
348−~fintud-macrep:dojo> :even &atom 18
349−[%even 18]
350−[%odd 9]
351−[%even 28]
352−[%even 14]
353−[%odd 7]
354−[%even 22]
355−[%odd 11]
356−[%even 34]
357−[%odd 17]
358−[%even 52]
359−[%even 26]
360−[%odd 13]
361−[%even 40]
362−[%even 20]
363−[%even 10]
364−[%odd 5]
365−[%even 16]
366−[%even 8]
367−[%even 4]
368−[%even 2]
369−%success
370−```
371−
372−- Put `even` and `odd` on two separate ships and pass the
373− messages over the network.
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+