on 39cf2ed help sign in
948 lines 21 kB history raw
1# Gall
3## Reference Agent
5This app stores a number for each user. You can increment your number, increment
6someone else's number, start tracking someone else's number by poking. It
7exposes a scry namespace to inspect everyone's number.
9### `app/example-gall.hoon`
11```hoon
12:: Import sur/example-gall
13/- *example-gall
14:: Import lib/default-agent
15/+ default-agent
16|%
17+$ card card:agent:gall
18+$ note
19 $%
20 [%arvo =note-arvo]
21 [%agent [=ship name=term] =task:agent:gall]
22 ==
23+$ state-zero [%0 local=@ud]
24+$ state-one [%1 local=@ud remote=(map @p @ud)]
25+$ versioned-state
26 $%
27 state-zero
28 state-one
29 ==
30--
31=| state-one
32=* state -
33^- agent:gall
34|_ =bowl:gall
35+* this .
36 def ~(. (default-agent this %|) bowl)
37::
38:: Set local counter to 1 by default
39++ on-init
40 ^- (quip card _this)
41 `this(local 1)
42:: Expose state for saving
43++ on-save
44 !>(state)
45::
46:: Load old state and upgrade if neccessary
47++ on-load
48 |= old=vase
49 ^- (quip card _this)
50 =/ loaded=versioned-state
51 !<(versioned-state old)
52 ?- -.loaded
53 %0
54 `this(local local.loaded) :: Upgrade old state
55 %1
56 `this(state loaded)
57 ==
58::
59:: Respond to poke
60:: App can be poked in the dojo by running the following commands
61:: Increment local counter
62:: :example-gall &example-gall-action [%increment ~]
63:: Increment ~zod's counter
64:: :example-gall &example-gall-action [%increment-remote ~zod]
65:: Subscribe to ~zod's counter
66:: :example-gall &example-gall-action [%view ~zod]
67:: Unsubscribe from ~zod's counter
68:: :example-gall &example-gall-action [%stop-view ~zod]
69::
70++ on-poke
71 |= [=mark =vase]
72 ^- (quip card _this)
73 :: Ensure poke is of mark %example-gall-action
74 ?> =(mark %example-gall-action)
75 =/ act=example-gall-action !<(example-gall-action vase)
76 ?- act
77 ::
78 :: Increment local counter and send new counter to subscribers
79 ::
80 [%increment ~]
81 :- [%give %fact `/local %atom !>(+(local))]~
82 this(local +(local))
83 ::
84 :: Send remote %increment poke
85 ::
86 [%increment-remote who=@p]
87 :_ this
88 :~ :*
89 %pass
90 /inc/(scot %p who.act)
91 %agent
92 [who.act %example-gall]
93 %poke %example-gall-action !>([%increment ~])
94 == ==
95 ::
96 :: Subscribe to a remote counter
97 ::
98 [%view who=@p]
99 :_ this
100 [%pass /view/(scot %p who.act) %agent [who.act %example-gall] %watch /local]~
101 ::
102 :: Unsubscribe from remote counter and remove from state
103 ::
104 [%stop-view who=@p]
105 :_ this(remote (~(del by remote) who.act))
106 [%pass /view/(scot %p who.act) %agent [who.act %example-gall] %leave ~]~
107 ::
108 ==
109::
110:: Print on unsubscribe
111::
112++ on-leave
113 |= =path
114 ^- (quip card _this)
115 ~& "Unsubscribed by: {<src.bowl>} on: {<path>}"
116 `this
117::
118:: Handle new subscription
119::
120:: When another ship subscribes to our counter, give them the current state of
121:: the counter immediately
122::
123++ on-watch
124 |= =path
125 ^- (quip card _this)
126 :_ this
127 :: Crash if we see a subscription we don't recognise
128 ?+ path ~|("unexpected subscription" !!)
129 ::
130 [%local ~]
131 [%give %fact ~ %atom !>(local)]~
132 ==
133::
134:: Expose scry namespace
135::
136:: .^(@ %gx /=example-gall=/local/atom) will produce the current local counter
137:: .^(@ %gx /=example-gall=/remote/~zod/atom) will produce the counter for ~zod
138:: .^(arch %gy /=example-gall=/remote) will produce a listing of the current
139:: remote counters
140++ on-peek
141 |= =path
142 ^- (unit (unit cage))
143 ?+ path [~ ~]
144 ::
145 :: Produce local counter
146 ::
147 [%x %local ~]
148 ``[%atom !>(local)]
149 ::
150 :: Produce remote counter
151 ::
152 [%x %remote who=@ta ~]
153 =* location i.t.t.path :: Ship name is third in the list
154 =/ res
155 (~(got by remote) (slav %p location))
156 ``[%atom !>(res)]
157 ::
158 :: Produce listing of remote counters
159 ::
160 [%y %remote ~]
161 =/ dir=(map @ta ~)
162 %- molt :: Map from list of k-v pairs
163 %+ turn :: iterate over list of k-v pairs
164 ~(tap by remote) :: list of k-v pairs from map
165 |= [who=@p *]
166 [(scot %p who) ~]
167 ``[%arch !>(`arch`[~ dir])]
168 ==
169::
170:: Handle sign from agent
171::
172++ on-agent
173 |= [=wire =sign:agent:gall]
174 ^- (quip card _this)
175 ?- -.sign
176 ::
177 :: Print error if poke failed
178 ::
179 %poke-ack
180 ?~ p.sign
181 `this
182 %- (slog u.p.sign)
183 `this
184 ::
185 :: Print error if subscription failed
186 ::
187 %watch-ack
188 ?~ p.sign
189 `this
190 =/ =tank leaf+"subscribe failed from {<dap.bowl>} on wire {<wire>}"
191 %- (slog tank u.p.sign)
192 `this
193 ::
194 :: Do nothing if unsubscribed
195 ::
196 %kick `this
197 ::
198 :: Update remote counter when we get a subscription update
199 ::
200 %fact
201 :- ~
202 ?. ?=(%atom p.cage.sign)
203 this
204 this(remote (~(put by remote) src.bowl !<(@ q.cage.sign)))
205 ==
206::
207:: Handle arvo signs
208::
209:: We never give any cards to arvo. Therefore we never need to handle any signs
210:: from arvo. We use the default-agent library to avoid implementing this arm,
211:: as gall apps must have all the arms.
212::
213++ on-arvo on-arvo:def
214::
215:: Handle error
216::
217:: Print errors when they happen
218::
219++ on-fail
220 |= [=term =tang]
221 ^- (quip card _this)
222 %- (slog leaf+"error in {<dap.bowl>}" >term< tang)
223 `this
224--
226```
228### `sur/example-gall.hoon`
230```hoon
231|%
232+$ example-gall-action
233 $%
234 [%view who=@p]
235 [%stop-view who=@p]
236 [%increment-remote who=@p]
237 [%increment ~]
238 ==
239--
240```
242### `mar/example-gall/action.hoon`
244```hoon
245/- *example-gall
246|_ act=example-gall-action
247++ grab
248 |%
249 ++ noun example-gall-action
250 --
251--
252```
254## Arms
256### `++on-init`
258Application initialisation
260`++on-init` is called when an application is first started. It is not a gate and
261has no input.
263#### Returns
265```hoon
266(quip card _this)
267```
269List of cards and new agent
271#### Example
273```hoon
274:: From reference agent.
275:: Set local counter to 1 by default
276::
277++ on-init
278 ^- (quip card _this)
279 `this(local.sta 1)
280:: From app/weather.hoon. This setup is typical of a landscape tile.
281::
282++ on-init
283 :_ this
284 :~ [%pass /bind/weather %arvo %e %connect [~ /'~weather'] %weather]
285 :* %pass /launch/weather %agent [our.bol %launch] %poke
286 %launch-action !>([%weather /weathertile '/~weather/js/tile.js'])
287 ==
288 ==
289```
291### `++on-save`
293Expose state for saving.
295This arm is called immediately before the agent is upgraded. It packages the
296permament state of the agent in a vase for the next version of the agent.
297Unlike most handlers, this cannot produce effects. It is not a gate and has no input.
299#### Returns
301`vase` of permanent state.
303#### Example
305```hoon
306:: From reference agent
307::
308:: Expose state for saving
309::
310++ on-save
311 !>(state)
312```
314### `++on-load`
316Application upgrade.
318This arm is called immediately after the agent is upgraded. It receives
319a vase of the state of the previously-running version of the agent, obtained
320from `+on-save`, which allows it to cleanly upgrade from the old agent.
322#### Accepts
324```hoon
325=vase
326```
328`vase` of previous state, from `++on-save`
330#### Returns
332```hoon
333(quip card _this)
334```
336List of cards and new agent
338#### Examples
340```hoon
341:: From reference agent
342::
343:: Load old state and upgrade if neccessary
344++ on-load
345 |= old=vase
346 ^- (quip card _this)
347 =/ loaded=versioned-state
348 !<(versioned-state old)
349 ?- -.loaded
350 %0
351 `this(local local.loaded) :: Upgrade old state
352 %1
353 `this(state loaded)
354 ==
355```
357### `++on-poke`
359Handle application poke.
361This arm is called when the agent is "poked". The input is a cage, so
362it's a pair of a mark and a dynamic vase.
364#### Accepts
366```hoon
367[=mark =vase]
368```
370`mark` is the mark of the poked data.
372`vase` is a vase with the poked data inside.
374#### Returns
376```hoon
377(quip card _this)
378```
380List of cards and new agent.
382#### Example
384```hoon
385:: From reference agent.
386::
387:: Respond to poke
388:: App can be poked in the dojo by running the following commands
389:: Increment local counter
390:: :example-gall &example-gall-action [%increment ~]
391:: Increment ~zod's counter
392:: :example-gall &example-gall-action [%increment-remote ~zod]
393:: Subscribe to ~zod's counter
394:: :example-gall &example-gall-action [%view ~zod]
395:: Unsubscribe from ~zod's counter
396:: :example-gall &example-gall-action [%stop-view ~zod]
397::
398++ on-poke
399 |= [=mark =vase]
400 ^- (quip card _this)
401 :: Ensure poke is of mark %example-gall-action
402 ?> =(mark %example-gall-action)
403 =/ act=example-gall-action !<(example-gall-action vase)
404 ?- act
405 ::
406 :: Increment local counter and send new counter to subscribers
407 ::
408 [%increment ~]
409 :- [%give %fact `/local %atom !>(+(local))]~
410 this(local +(local))
411 ::
412 :: Send remote %increment poke
413 ::
414 [%increment-remote who=@p]
415 :_ this
416 :~ :*
417 %pass
418 /inc/(scot %p who.act)
419 %agent
420 [who.act %example-gall]
421 %poke %example-gall-action !>([%increment ~])
422 == ==
423 ::
424 :: Subscribe to a remote counter
425 ::
426 [%view who=@p]
427 :_ this
428 [%pass /view/(scot %p who.act) %agent [who.act %example-gall] %watch /local]~
429 ::
430 :: Unsubscribe from remote counter and remove from state
431 ::
432 [%stop-view who=@p]
433 :_ this(remote (~(del by remote) who.act))
434 [%pass /view/(scot %p who.act) %agent [who.act %example-gall] %leave ~]~
435 ::
436 ==
437```
439### `++on-watch`
441Handle new subscriber.
443This arm is called when a program wants to subscribe to the agent on a
444particular path. The agent may or may not need to perform setup steps
445to intialize the subscription. It may produce a `%give`
446`%subscription-result` to the subscriber to get it up to date, but after
447this event is complete, it cannot give further updates to a specific
448subscriber. It must give all further updates to all subscribers on a
449specific path.
451If this arm crashes, then the subscription is immediately terminated.
452More specifcally, it never started -- the subscriber will receive a
453negative `%watch-ack`. You may also produce an explicit `%kick` to
454close the subscription without crashing -- for example, you could
455produce a single update followed by a `%kick`.
457#### Accepts
459```hoon
460=path
461```
463Path of new subscription.
465#### Returns
467```hoon
468(quip card _this)
469```
471List of cards and new agent.
473#### Example
475```hoon
476:: From reference agent
477::
478:: Handle new subscription
479::
480:: When another ship subscribes to our counter, give them the current state of
481:: the counter immediately
482::
483++ on-watch
484 |= =path
485 ^- (quip card _this)
486 :_ this
487 ?+ path on-watch:def
488 ::
489 [%local ~]
490 [%give %fact ~ %atom !>(local)]~
491 ==
492```
494### `++on-leave`
496Handle unsubscribe.
498This arm is called when a program becomes unsubscribed to you.
499Subscriptions may close because the subscriber intentionally
500unsubscribed, but they also could be closed by an intermediary. For
501example, if a subscription is from another ship which is currently
502unreachable, Ames may choose to close the subscription to avoid queueing
503updates indefinitely. If the program crashes while processing an
504update, this may also generate an unsubscription. You should consider
505subscriptions to be closable at any time.
507#### Accepts
509```hoon
510=path
511```
513Path of the closed subscription.
515#### Returns
517```hoon
518(quip card _this)
519```
521List of cards and new agent.
523#### Example
525```hoon
526:: From reference agent. Prints a message when programs become unsubscribed.
527++ on-leave
528 |= =path
529 ^- (quip card _this)
530 ~& "Unsubscribe by: {<src.bowl>} on: {<path>}"
531 `this
532```
534### `++on-peek`
536Handle scry request.
538This arm is called when a program reads from the agent's "scry"
539namespace, which should be referentially transparent. Unlike most
540handlers, this cannot perform IO, and it cannot change the state. All
541it can do is produce a piece of data to the caller, or not.
543#### Accepts
545```hoon
546=path
547```
549The path being scryed for.
551```hoon
552:: Example scry to path mappings
553::
554.^(arch %gy /=example-gall=/remote)
555:: Path will be /y/remote
556.^(@ %gx /=example-gall=/local/atom)
557:: Path will be /x/local
558```
560#### Returns
562```hoon
563(unit (unit cage))
564```
566If this arm produces `[~ ~ data]`, then `data` is the value at the the
567given path. If it produces `[~ ~]`, then there is no data at the given
568path and never will be. If it produces `~`, then we don't know yet whether
569there is or will be data at the given path. The head of the path is known as the
570`care`. Requests with a care of `%x` should return a vase that matches or is
571convertible to the mark at the end of the scry request. This mark is not
572included in the path passed to `++on-peek`. Requests with a care of `%y` should
573return a cage with a mark of `%arch` and a vase of `arch`.
575#### Example
577```hoon
578:: From reference agent
579::
580:: Expose scry namespace
581::
582:: .^(@ %gx /=example-gall=/local/atom) will produce the current local counter
583:: .^(@ %gx /=example-gall=/remote/~zod/atom) will produce the counter for ~zod
584:: .^(arch %gy /=example-gall=/remote) will produce a listing of the current
585:: remote counters
586++ on-peek
587 |= =path
588 ^- (unit (unit cage))
589 ?+ path [~ ~]
590 ::
591 :: Produce local counter
592 ::
593 [%x %local ~]
594 ``[%atom !>(local)]
595 ::
596 :: Produce remote counter
597 ::
598 [%x %remote who=@ta ~]
599 =* location i.t.t.path :: Ship name is third in the list
600 =/ res
601 (~(got by remote) (slav %p location))
602 ``[%atom !>(res)]
603 ::
604 :: Produce listing of remote counters
605 ::
606 [%y %remote ~]
607 =/ dir=(map @ta ~)
608 %- molt :: Map from list of k-v pairs
609 %+ turn :: iterate over list of k-v pairs
610 ~(tap by remote) :: list of k-v pairs from map
611 |= [who=@p *]
612 [(scot %p who) ~]
613 ``[%arch !>(`arch`[~ dir])]
614 ==
615```
617### `++on-agent`
619Handle `%pass` card
621This arm is called to handle responses to `%pass` cards to other agents.
622It will be one of the following types of response:
624- `%poke-ack`: acknowledgment (positive or negative) of a poke. If the
625 value is `~`, then the poke succeeded. If the value is `[~ tang]`,
626 then the poke failed, and a printable explanation (eg a stack trace)
627 is given in the `tang`.
629- `%watch-ack`: acknowledgment (positive or negative) of a subscription.
630 If negative, the subscription is already ended (technically, it never
631 started).
633- `%fact`: update from the publisher.
635- `%kick`: notification that the subscription has ended. This happens because
636 either the target app passed a `%leave` note, or ames killed the subscription
637 due to backpressure. Most of the time you will want to resubscribe. If you can
638 no longer access the subscription you will get a negative `%watch-ack` and end
639 your flow there.
641#### Accepts
643```hoon
644[=wire =sign:agent:gall]
645```
647`wire` is the wire from the `+gift` that triggered `++on-agent`
649`sign` is response for the gift.
651#### Returns
653```hoon
654(quip card _this)
655```
657#### Example
659```hoon
660:: From reference agent
661::
662:: Handle sign from agent
663::
664++ on-agent
665 |= [=wire =sign:agent:gall]
666 ^- (quip card _this)
667 ?- -.sign
668 ::
669 :: Print error if poke failed
670 ::
671 %poke-ack
672 ?~ p.sign
673 `this
674 %- (slog u.p.sign)
675 `this
676 ::
677 :: Print error if subscription failed
678 ::
679 %watch-ack
680 ?~ p.sign
681 `this
682 =/ =tank leaf+"subscribe failed from {<dap.bowl>} on wire {<wire>}"
683 %- (slog tank u.p.sign)
684 `this
685 ::
686 :: Do nothing if unsubscribed
687 ::
688 %kick `this
689 ::
690 :: Update remote counter when we get a subscription update
691 ::
692 %fact
693 :- ~
694 ?. ?=(%atom p.cage.sign)
695 this
696 this(remote (~(put by remote) src.bowl !<(@ q.cage.sign)))
697 ==
698```
700### `++on-arvo`
702Handle vane response
704This arm is called to handle responses for `%pass` cards to vanes.
706#### Accepts
708```hoon
709 [=wire =sign:agent:gall]
710```
712`wire` is the wire from the `++gift` that triggered `++on-arvo`.
714`sign` is the response from the vane. The list of possible responses from the
715vanes is statically defined in sys/zuse.hoon (grep for `++ sign-arvo`).
717#### Returns
719```hoon
720(quip card _this)
721```
723List of cards and new agent.
725#### Example
727```hoon
728:: From app/weather.hoon. Handles %bound, %wake and %http-response signs
729++ on-arvo
730 |= [=wire =sign-arvo]
731 ^- (quip card:agent:gall _this)
732 ?: ?=(%bound +<.sign-arvo)
733 [~ this]
734 ?: ?=(%wake +<.sign-arvo)
735 =^ cards state
736 (wake:wc wire error.sign-arvo)
737 [cards this]
738 ?: ?=(%http-response +<.sign-arvo)
739 =^ cards state
740 (http-response:wc wire client-response.sign-arvo)
741 [cards this]
742 (on-arvo:def wire sign-arvo)
743::
744:: From reference agent
745::
746:: Handle arvo signs
747::
748:: We never give any cards to arvo. Therefore we never need to handle any signs
749:: from arvo. We use the default-agent library to avoid implementing this arm,
750:: as gall apps must have all the arms specified in the agent:gall definition.
751::
752++ on-arvo on-arvo:def
753```
755### `++on-fail`
757Handle error.
759If an error happens in `+on-poke`, the crash report goes into the
760`%poke-ack` response. Similarly, if an error happens in
761`+on-subscription`, the crash report goes into the `%watch-ack`
762response. If a crash happens in any of the other handlers, the report
763is passed into this arm.
765#### Accepts
767```hoon
768[=term =tang]
769```
771`term` is a cord describing the error.
773`tang` is a stack trace for the error.
775#### Returns
777```hoon
778(quip card _this)
779```
781List of cards and new agent
783#### Example
785```hoon
786::
787:: Handle error
788::
789:: Print errors when they happen
790::
791++ on-fail
792 |= [=term =tang]
793 ^- (quip card _this)
794 %- (slog leaf+"error in {<dap.bowl>}" >term< tang)
795 `this
796```
798## Agent Gifts
800Giving a gift takes the general form of
802```hoon
803[%give =gift]
804```
806### `%fact`
808Produce a subscription update.
810Produces a subscription update. A subscription update is a new piece of
811subscription content for all subscribers on a given path.
813#### Structure
815```hoon
816[%fact (unit path) =cage]
817```
819`(unit path)` is the path of the subscription being updated. If no path is
820 given, then the update is only given to the program that instigated the
821 request. Typical use of this mode is in `+on-watch` to give an initial update
822 to a new subscriber to get them up to date.
824`cage` is a cage of the subscription update.
826#### Example
828```hoon
829:: From ++on-watch in reference agent.
830::
831:: Gives current local state to new subscribers.
832[%give %fact ~ %atom !>(local.sta)]
833```
835### `%kick`
837Close subscription.
839Closes a subscription. A subscription close closes the subscription for all or
840one subscribers on a given path.
842#### Structure
844```hoon
845[%kick (unit path) (unit ship)]
846```
848`(unit path)` is the path of the subscription being updated. If no path is
849given, then the update is only given to the program that instigated the request.
850Typical use of this mode would be in `+on-watch` to produce a single update to a
851subscription then close the subscription.
853`(unit ship)` is the ship to close the subscription for. If no path is given,
854then the subscription is closed for all subscribers.
856## Agent Notes
858Passing a agent note (a 'task') along a wire looks like so.
860```hoon
861[%pass =wire %agent [=ship name=term] =task]
862```
864`wire` is used to identify the response to the note.
866`ship` is the ship to pass the note to.
868`name` is the name of the agent that should receive the note.
870`task` is the task itself, described below.
872### `%poke`
874Poke an application.
876This note is passed to poke an application with a cage, a marked vase.
878#### Structure
880```hoon
881[%poke =cage]
882```
884`cage` is the marked data to poke the application with. It is a pair of a mark
885and vase.
887#### Example
889```hoon
890:: From ++on-poke in reference agent.
891::
892:: Sends an increment poke to the example-gall agent
893:: on who.act.
894:*
895 %pass
896 /inc/(scot %p who.act)
897 %agent
898 [who.act %example-gall]
899 %poke %example-gall-action !>([%increment ~])
900==
901```
903### `%watch`
905Subscribe to an application.
907This note is given to subscribe to an application at a path
909#### Structure
911```hoon
912[%watch =path]
913```
915`path` is the path to be subscribed to.
917#### Example
919```hoon
920:: From ++on-poke in reference agent.
921::
922:: Subscribes to the example-gall agent on who.act on the path /local
923::
924[%pass /view/(scot %p who.act) %agent [who.act %example-gall] %watch /local]
925```
927### `%leave`
929Unsubscribe from an application.
931 This note is passed to unsubscribe from an application. It should be passed on
932the same wire that the corresponding `%watch` note for the subscription was
933passed on.
935#### Structure
937```hoon
938[%leave ~]
939```
941#### Example
943```hoon
944:: From ++on-poke in reference agent.
945::
946:: Unsubscribes from the example-gall agent on who.act
947[%pass /view/(scot %p who.act) %agent [who.act %example-gall] %leave ~]
948```