on cd13397 help sign in
urbit.org/tutorials/hoon/egg-timer.md
159 lines 6.8 kB history raw
1+++
2title = "2.7.1 Gall Walkthrough: Egg Timer"
3weight = 36
4template = "doc.html"
5aliases = ["/docs/learn/hoon/hoon-tutorial/egg-timer/"]
6+++
8The Arvo operating system is divided up into modules called vanes. Gall is the vane responsible for providing an API for building stateful applications in Urbit. Gall is a complex system that takes a lot of getting used to, so let's dip our toes in with a simple egg-timer app.
10The following code sample should be saved as `/home/app/egg-timer.hoon`
11After committing: `|start %egg-timer` and poke with `:egg-timer ~s10`
13```hoon
14/+ default-agent
15|%
16+$ card card:agent:gall
17--
18::
19^- agent:gall
20|_ =bowl:gall
21+* this .
22 def ~(. (default-agent this %.n) bowl)
23::
24++ on-poke
25 |= [=mark =vase]
26 ^- (quip card _this)
27 =/ t !<(@dr vase)
28 :_ this
29 [%pass /egg-timer %arvo %b %wait (add now.bowl t)]~
30::
31++ on-arvo
32 ^+ on-arvo:*agent:gall
33 |= [=wire =sign-arvo]
34 ^- (quip card _this)
35 ~& "Timer went off!"
36 [~ this]
37::
38++ on-init on-init:def
39++ on-save on-save:def
40++ on-load on-load:def
41++ on-watch on-watch:def
42++ on-leave on-leave:def
43++ on-peek on-peek:def
44++ on-agent on-agent:def
45++ on-fail on-fail:def
46--
47```
49The first thing to notice is that we are creating a core (`|%`) and a door (`|_`). This is a typical style for Gall programming wherein types are defined in the first core and your application is defined in the following door.
50Our door will have type `agent:gall` as specified in `sys/zuse.hoon`
52```hoon
53|%
54+$ card card:agent:gall
55--
56```
58The core here defines only one type: `card`.
59A `card` is a request to Arvo to do something for us. In this case, the only card used will be a `%wait` request to Behn which we'll discuss later.
61It's important to note that the name `card` is technically arbitrary. By convention, however, you will see it called `card` practically everywhere.
63The sample of the door(`|_`) is:
65```hoon
66=bowl:gall
67```
69You can find the full definition of `bowl` in `sys/zuse.hoon`, but for now it's enough to know that includes contextual data used by Gall with particular faces. Below are some important such faces:
71- `our` The ship this code is running on
72- `src` The ship the current event originated from
73- `eny` Guaranteed-fresh entropy
74- `now` The current time
76We give `bowl:gall` the face `bowl`.
77Additionally, we use `+*` to create two structures for future use:
79```hoon
80+* this .
81 def ~(. (default-agent this %.n) bowl)
82```
83`this` is our current context(the door).
84`def` uses `default-agent` to produce a standard `gall:agent` door, from which we will instance default definitions for our unused arms.
86All Gall agents have a fixed set of 10 arms. In this trivial case, we will specify only two and use the default definitions from `default-agent` for the remaining arms.
88```hoon
89++ on-poke
90 |= [=mark =vase]
91 ^- (quip card _this)
92 =/ t !<(@dr vase)
93 :_ this
94 [%pass /egg-timer %arvo %b %wait (add now.bowl t)]~
95```
97The `++on-poke` arm evaluates to a gate which takes a sample `[mark vase]`. `mark` is a type specification and `vase` is a cell of `[specification value]`
98Note: since `++on-poke` may take many different samples, it would be prudent to add a `%noun` mark check here.
99In this example, we assume that our sample will contain a `@dr` and pull it from the `vase` with `=/ t !<(@dr vase)`
100`@dr` is an aura for a 128-bit relative date. Here are a few examples.
102- `~s17` 17 seconds
103- `~m20` 20 minutes
104- `~m13s37` 13 minutes, 37 seconds
105- `~d42` 42 days
107As a matter of good type hygiene, we explicitly cast the output of this gate with `^-` to ensure we are producing the correct structure for Gall to handle.
108Our specification is `(quip card _this)` which produces a type, `[(list card) _this]`. Note that `_this` is irregular form of `$_(this)` which normalizes to the type of `this`, our door.
110Next we use the `:_` rune which is the inverted form of `:-`, the cell construction rune.
112```hoon
113:_ this
114 [%pass /egg-timer %arvo %b %wait (add now.bowl t)]~
115```
116Our card should follow the basic form `[%pass /my/wire a-note]`
117In this case, our card will ask Arvo to `%pass` a note along the wire `/egg-timer` to Behn(`%b`) containing a `%wait` request.
118Behn will start a timer for us, and will respond along our wire, `/egg-timer`.
120`now.bowl` is the current time of type `@da`, and `t` was declared as `@dr`. Because they are both atoms, we can add `now` and `t` to produce an atom that is `t` units of time into the future from `now`. That produced atom can be interpreted as a `@da`.
121The calculated time will be passed via Arvo to Behn with instruction to `%wait @da`, i.e., to set a timer.
123That's all for our `++on-poke` arm. But what about when the timer goes off? Behn will create a `gift`, a similar construct to our `card` which will be dispatched back to us via Gall in the `++on-arvo` arm.
125```hoon
126++ on-arvo
127 ^+ on-arvo:*agent:gall
128 |= [=wire =sign-arvo]
129 ^- (quip card _this)
130 ~& "Timer went off!"
131 [~ this]
132```
133`++on-arvo` evaluates to a gate whose sample is a cell of `wire` and `sign-arvo` here given default faces. The syntax of `=wire` is a shortcut for `wire=wire`; it's a common pattern to shadow the name of a type when you only have one instance of the type and are not going to refer to the type itself.
134A `wire` is an alias for a path. Our `wire` will be the path we provided in our `++on-poke` card. If we needed to do something particular depending on which request triggered this Arvo card, we could use the wire to do so.
136Note: since multiple vanes can send many sorts of cards via `++on-arvo`, it would be prudent to check if `sign-arvo` is `[%b %wake *]`. Since our trivial example assumes the card is `%wake` from Behn, we will forgo that better practice here.
138Next we have the same cast we used in `++on-poke` to make sure we produce the correct type for Gall. `(quip card _this)` resolves to a type `[(list card) _this]`
139```
140~& "Timer went off!"
141```
143`~&` is the debugging printf rune. Here we're using it to output a message. We could, however, modify this line of code to do any other computation when `++on-arvo` is called.
145We need to produce a noun of type `(quip card _this)` i.e., `[(list card) _this]`.
146In this case, we will not produce any card or change application state. Therefore we simply produce a cell of a null list and our original context, `[~ this]`.
148```hoon
149++ on-init on-init:def
150++ on-save on-save:def
151++ on-load on-load:def
152++ on-watch on-watch:def
153++ on-leave on-leave:def
154++ on-peek on-peek:def
155++ on-agent on-agent:def
156++ on-fail on-fail:def
157--
158```
159Finally, we define the remaining 8 unused but required Gall Agent armsas instances of the default arms provided in `/lib/default-agent.hoon`.