on master help sign in
Merge pull request #859 from risruc-habteb/master
89e5fbdDavid Kerschner
tutorials/hoon/egg-timer.md+99−59
@@ -5,115 +5,155 @@ template = "doc.html"
5aliases = ["/docs/learn/hoon/hoon-tutorial/egg-timer/"]
5aliases = ["/docs/learn/hoon/hoon-tutorial/egg-timer/"]
6+++
6+++
7
7
8−The 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.
8+The 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.
9
9
10+The following code sample should be saved as `/home/app/egg-timer.hoon`
11+After committing: `|start %egg-timer` and poke with `:egg-timer ~s10`
10
12
11```hoon
13```hoon
14+/+ default-agent
12|%
15|%
13−+$ move (pair bone card)
16++$ card card:agent:gall
14−+$ card [%wait path @da]
15--
17--
18+::
19+^- agent:gall
16−|_ [bowl:gall ~]
20+|_ =bowl:gall
21++* this .
22+ def ~(. (default-agent this %.n) bowl)
23+::
17−++ poke-noun
24+++ on-poke
25+ |= [=mark =vase]
26+ ^- (quip card _this)
18− |= t=@dr
27+ =/ t !<(@dr vase)
19− ^+ [*(list move) +>.$]
28+ :_ this
20− :_ +>.$ :_ ~
21− [ost %wait /egg-timer (add now t)]
29+ [%pass /egg-timer %arvo %b %wait (add now.bowl t)]~
22−++ wake
30+::
23− |= [=wire error=(unit tang)]
31+++ on-arvo
24− ^+ [*(list move) +>.$]
32+ ^+ on-arvo:*agent:gall
33+ |= [=wire =sign-arvo]
34+ ^- (quip card _this)
25 ~& "Timer went off!"
35 ~& "Timer went off!"
26− [~ +>.$]
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
27--
46--
28```
47```
29
48
30−The first thing to notice is that we are creating a `core` (`|%`) and a `door` (`|_`). This is a typical style of Gall programming where your types are defined in the first `core` and your application is defined in the following `door`.
49+The 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.
50+Our door will have type `agent:gall` as specified in `sys/zuse.hoon`
31
51
32```hoon
52```hoon
33|%
53|%
34−+$ move (pair bone card)
54++$ card card:agent:gall
35−+$ card [%wait path @da]
36--
55--
37```
56```
38
57
58+The core here defines only one type: `card`.
39−The `core` here defines two types: `move` and `card`. A `move` is a `pair` of `bone` and `card`. A `bone` is a Gall-only type that identifies app event chains by mapping atoms to them. We define `card` to be a request to Arvo to do something for us. In this case, the only valid `card` will be `%wait` which we'll discuss in a bit.
59+A `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.
40
60
41−It's important to note that the names `move` and `card` are technically arbitrary; you can call them whatever you'd like. By convention, however, you will see them called `move` and `card` practically everywhere.
61+It's important to note that the name `card` is technically arbitrary. By convention, however, you will see it called `card` practically everywhere.
42
62
43−The sample of the `door` is:
63+The sample of the door(`|_`) is:
44
64
45```hoon
65```hoon
46−[bowl:gall ~]
66+=bowl:gall
47```
67```
48
68
49−You can find the full definition of `bowl` in `sys/zuse.hoon`, but for now it's enough to know that this is the default app state and includes various faces for information. Below are some important such faces:
69+You 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:
50
70
51- `our` The ship this code is running on
71- `our` The ship this code is running on
52- `src` The ship the current event originated from
72- `src` The ship the current event originated from
53−- `ost` A reference to the current chain of events
54- `eny` Guaranteed-fresh entropy
73- `eny` Guaranteed-fresh entropy
55- `now` The current time
74- `now` The current time
56
75
57−The `door` we've made has two [arms](/docs/glossary/arm/) `poke-noun` and `wake`. Gall is capable of dispatching `pokes`, or requests, to an app based on the mark of the data given along with that poke. These are sent to the [arm](/docs/glossary/arm/) with the name that matches the mark of the data. Here we use the generic `noun` mark:
76+We give `bowl:gall` the face `bowl`.
77+Additionally, we use `+*` to create two structures for future use:
58
78
59```hoon
79```hoon
60−++ poke-noun
80++* this .
61− |= t=@dr
81+ def ~(. (default-agent this %.n) bowl)
62− ^+ [*(list move) +>.$]
63− :_ +>.$ :_ ~
64− [ost %wait /egg-timer (add now t)]
65```
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.
66
85
67−In the above code, we create a [gate](/docs/glossary/gate/) that takes a single `@dr` argument. `@dr` is an aura for a 128-bit relative date. Here are a few examples.
86+All 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.
68−
69−- `~s17` 17 seconds
70−- `~m20` 20 minutes
71−- `~d42` 42 days
72−
73−As a matter of good type hygiene, we explicitly cast the output of this gate with `^+` to ensure we are producing the correct thing for Gall to handle. `^+` is the rune for casting by example. Our example is a cell: `list` of `move`, which we `bunt` with `*`, is the head; `+>.$`, the enclosing [core](/docs/glossary/core/) which is our `door`, is the tail.
74−
75−Next we're going to use the `:_` rune which is just the inverted form a `:-` the cell construction rune. We use it twice so the actual data will end up looking something like:
76
87
77```hoon
88```hoon
89+++ on-poke
90+ |= [=mark =vase]
91+ ^- (quip card _this)
92+ =/ t !<(@dr vase)
93+ :_ this
78−[[[ost %wait /egg-timer (add now t)] ~] +>.$]
94+ [%pass /egg-timer %arvo %b %wait (add now.bowl t)]~
79```
95```
80
96
81−`ost` is the `bone`, the opaque reference to a chain of events, that comes from `bowl`, so we're going to continue to use it here. `%wait` is the name of the `move`, in this case a request to the Behn vain to start a timer for us.
97+The `++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]`
98+Note: since `++on-poke` may take many different samples, it would be prudent to add a `%noun` mark check here.
99+In 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.
82
101
83−After `%wait`, we have the `path`, a `list` of `cords` that serves as the unique identifier for this `move`:
102+- `~s17` 17 seconds
103+- `~m20` 20 minutes
104+- `~m13s37` 13 minutes, 37 seconds
105+- `~d42` 42 days
84
106
85−```
107+As 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.
86−/egg-timer
108+Our 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.
87−```
88−
89−The `/` here is an irregular syntax to make a `path`.
90
109
91−The final part of this `move` is:
110+Next we use the `:_` rune which is the inverted form of `:-`, the cell construction rune.
92
111
112+```hoon
113+:_ this
114+ [%pass /egg-timer %arvo %b %wait (add now.bowl t)]~
93```
115```
94−(add now t)
116+Our card should follow the basic form `[%pass /my/wire a-note]`
95−```
117+In this case, our card will ask Arvo to `%pass` a note along the wire `/egg-timer` to Behn(`%b`) containing a `%wait` request.
118+Behn will start a timer for us, and will respond along our wire, `/egg-timer`.
96
119
97−`now` is the current time of type `@da`, and `t` was declared as `@dr`. Because they are both atoms, we can add `now` and `t` these two to get an atom that is `t` units of time into the future from `now`. That produced atom can be interpreted as a `@da`.
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`.
121+The calculated time will be passed via Arvo to Behn with instruction to `%wait @da`, i.e., to set a timer.
98
122
99−That's all for our `poke-noun` arm. But what about when the timer goes off? Behn will create a `gift`, a similar construct to how we created a `card`, only this time it will end up being dispatched back to us via Gall in the `++wake` arm. Any app that wants to use a timer trigger needs to have an arm called `++wake`.
123+That'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.
100
124
101```hoon
125```hoon
102−++ wake
126+++ on-arvo
103− |= [=wire error=(unit tang)]
127+ ^+ on-arvo:*agent:gall
104− ^+ [*(list move) +>.$]
128+ |= [=wire =sign-arvo]
129+ ^- (quip card _this)
105 ~& "Timer went off!"
130 ~& "Timer went off!"
106− [~ +>.$]
131+ [~ this]
107```
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.
134+A `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.
108
135
109−`wake` is a `gate` that has two arguments: a `wire`, and a `(unit tang)` with the face `error`. 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. A `wire` is just an alias for `path`. Our `wire` will be the `path` that we gave original `move`. If we needed to do something based on which request caused this gate to be called, we could use the wire to do so. In this case, we don't do perform such a dispatch.
136+Note: 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.
110−
111−Next we have the same cast we used in `++poke-noun` to make sure we are producing the correct thing for Gall. These casts are not strictly necessary, as the type system can infer what the type will be, but they can be very useful both for debugging our own code and for someone else trying to determine what our code should be producing.
112
137
138+Next 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]`
113```
139```
114~& "Timer went off!"
140~& "Timer went off!"
115```
141```
116
142
117−`~&` 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 we would want to happen when `++wait` gets called.
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.
144+
145+We need to produce a noun of type `(quip card _this)` i.e., `[(list card) _this]`.
146+In 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]`.
118
147
119−Finally, we need to produce our `move`. Here the effect is simply `[~ +>.$]`, since we have no `move` to make and we are not changing the state of our app. We just use the `door` without changing its sample.
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+```
159+Finally, we define the remaining 8 unused but required Gall Agent armsas instances of the default arms provided in `/lib/default-agent.hoon`.