@@ -4,25 +4,25 @@ weight = 20
4template = "doc.html"
4template = "doc.html"
7−In the last lesson we look at some useful bits and pieces that are used as
7+In the last lesson we look at a couple of useful things used as boilerplate in
8−boilerplate in most agents. Now we're going to get into the guts of how agents
8+most agents. Now we're going to get into the guts of how agents work, and start
9−work, and start looking at what the agent arms do. The first thing we'll look at
9+looking at what the agent arms do. The first thing we'll look at is the agent's
10−is the agent's state, and the three arms for managing it: `on-init`, `on-save`,
10+state, and the three arms for managing it: `on-init`, `on-save`, and `on-load`.
11−and `on-load`. These arms handle what we call an agent's "lifecycle".
11+These arms handle what we call an agent's "lifecycle".
13## Lifecycle
13## Lifecycle
15An agent's lifecycle starts when it's first installed. At this point, the
15An agent's lifecycle starts when it's first installed. At this point, the
16−agent's `on-init` arm is called. This is the _only_ time its `on-init` arm is
16+agent's `on-init` arm is called. This is the _only_ time `on-init` is ever
17−ever called - its purpose is just to initialize the agent. The `on-init` arm
17+called - its purpose is just to initialize the agent. The `on-init` arm might be
18−might be very simple and just set an initial value for the state, or even do
18+very simple and just set an initial value for the state, or even do nothing at
19−nothing at all and return the agent core exactly as-is. It may also be more
19+all and return the agent core exactly as-is. It may also be more complicated,
20−complicated, and perform some [scries](/docs/arvo/concepts/scry) to obtain extra
20+and perform some [scries](/docs/arvo/concepts/scry) to obtain extra data or
21−data or check that another agent is also installed. It might send off some
21+check that another agent is also installed. It might send off some `card`s to
22−`card`s to other agents or vanes to do things like load data in to the
22+other agents or vanes to do things like load data in to the `%settings-store`
23−`%settings-store` agent, bind an Eyre endpoint, or anything else. It all depends
23+agent, bind an Eyre endpoint, or anything else. It all depends on the needs of
24−on the needs of a particular agent. If `on-init` fails for whatever reason, the
24+your particular application. If `on-init` fails for whatever reason, the agent
25−agent installation will fail and be aborted.
25+installation will fail and be aborted.
27Once initialized, an agent will just go on doing its thing - processing events,
27Once initialized, an agent will just go on doing its thing - processing events,
28updating its state, producing effects, etc. At some point, you'll likely want to
28updating its state, producing effects, etc. At some point, you'll likely want to
@@ -33,10 +33,9 @@ so you commit a modified version of the file to Clay. When this happens, three t
33- The agent's `on-save` arm is called, which packs the agent's state in a `vase`
33- The agent's `on-save` arm is called, which packs the agent's state in a `vase`
34 and exports it.
34 and exports it.
35- The new version of the agent is built and loaded into Gall.
35- The new version of the agent is built and loaded into Gall.
36−- The previously exported `vase` containing the old state is passed to the
36+- The previously exported `vase` is passed to the `on-load` arm of the newly
37− `on-load` arm of the newly built agent. The `on-load` arm will process it,
37+ built agent. The `on-load` arm will process it, convert it to the new version
38− convert it to the new version of the state if necessary, and load it back
38+ of the state if necessary, and load it back into the state of the agent.
39− into the state of the agent.
41A `vase` is just a cell of `[type-of-the-noun the-noun]` - it's used extensively
40A `vase` is just a cell of `[type-of-the-noun the-noun]` - it's used extensively
42for sending data between agents, vanes and ships. A vase is made with the
41for sending data between agents, vanes and ships. A vase is made with the
@@ -63,9 +62,9 @@ the type of the agent's state. In principle, we could make it as simple as this:
64However, when you update your agent as described in the [Lifecycle](#lifecycle)
63However, when you update your agent as described in the [Lifecycle](#lifecycle)
65section, you may want to change the type of the state itself. This means
64section, you may want to change the type of the state itself. This means
66−`on-load` might find different data types in the vase it receives, depending on
65+`on-load` might find old versions of the state in the `vase` it receives. Over
67−the situation. Over time, as you make further changes to the type of the state,
66+time, as you make further changes to the type of the state, this becomes quite
68−this becomes quite complicated.
67+complicated.
70For example, if you were creating an agent for a To-Do task management app, your
69For example, if you were creating an agent for a To-Do task management app, your
71tasks might initially have a simple `?` to specify whether they're complete or
70tasks might initially have a simple `?` to specify whether they're complete or
@@ -86,10 +85,12 @@ The conventional way to keep this managable is to have _versioned states_. The
86first version of the state would typically be called `state-0`, and its head
85first version of the state would typically be called `state-0`, and its head
87would be tagged with `%0`. Then, when you change the state's type in an update,
86would be tagged with `%0`. Then, when you change the state's type in an update,
88you'd add a new structure called `state-1` and tag its head with `%1`. The next
87you'd add a new structure called `state-1` and tag its head with `%1`. The next
89−would then be `state-2`, and so on. In addition to each of those individual
88+would then be `state-2`, and so on.
90+In addition to each of those individual state versions, you'd also define a
90−state versions, you'd also define a structure called `versioned-state`, which
91+structure called `versioned-state`, which just contains a union of all the
91−just contains a union of all the possible states. This way, the vase `on-load`
92+possible states. This way, the vase `on-load` receives can be unpacked to a
92−receives can be unpacked to a `versioned-state` type, and then a
93+`versioned-state` type, and then a
93[wuthep](/docs/hoon/reference/rune/wut#--wuthep) (`?-`) expression can switch on
94[wuthep](/docs/hoon/reference/rune/wut#--wuthep) (`?-`) expression can switch on
94the head (`%0`, `%1`, `%2`, etc) and process each one appropriately.
95the head (`%0`, `%1`, `%2`, etc) and process each one appropriately.
@@ -141,11 +142,13 @@ then on the arms of our agent will just modify it.
141## State management arms
142## State management arms
143We've described the basic lifecycle process and the purpose of each state
144We've described the basic lifecycle process and the purpose of each state
144−management arm. Now let's give an overview of each arm separately.
145+management arm. Now let's look at each arm in detail:
146### `on-init`
147### `on-init`
149+This arm takes no argument, and produces a `(quip card _this)`. It's called
148−This arm takes no argument, and produces a `(quip card _this)`. It's called exactly once, when the agent is first installed, and its purpose is to initialize the agent.
150+exactly once, when the agent is first installed. Its purpose is to initialize
150`(quip a b)` is equivalent to `[(list a) b]`, see the [types
153`(quip a b)` is equivalent to `[(list a) b]`, see the [types
151reference](/docs/userspace/gall-guide/types#quip) for details.
154reference](/docs/userspace/gall-guide/types#quip) for details.
@@ -177,12 +180,12 @@ suspended or an app is uninstalled, so that the state can be restored when it's
177resumed or reinstalled.
180resumed or reinstalled.
179The state is packed in a vase with the
182The state is packed in a vase with the
180−[zapgar](/docs/hoon/reference/rune/zap#-zapgar) (`!>`) rune like `!>(state)`.
183+[zapgar](/docs/hoon/reference/rune/zap#-zapgar) (`!>`) rune, like `!>(state)`.
182### `on-load`
185### `on-load`
184This arm takes a `vase` and produces a `(quip card _this)`. Its purpose is to
187This arm takes a `vase` and produces a `(quip card _this)`. Its purpose is to
185−import an agent state previously exported with [`on-save`](#on-save). Typically
188+import a state previously exported with [`on-save`](#on-save). Typically
186you'd have used a [versioned state](#versioned-state-type) as described above,
189you'd have used a [versioned state](#versioned-state-type) as described above,
187so this arm would test which state version the imported data has, convert data
190so this arm would test which state version the imported data has, convert data
188from an old version to the new version if necessary, and load it into the
191from an old version to the new version if necessary, and load it into the
@@ -251,9 +254,9 @@ state core:
252In `state-0` we've defined the structure of our state, which is just a `@ud`.
255In `state-0` we've defined the structure of our state, which is just a `@ud`.
253We've tagged the head with a `%0` constant representing the version number, so
256We've tagged the head with a `%0` constant representing the version number, so
254−`on-load` can easily test the version of data. In `versioned-state` we've
257+`on-load` can easily test the state version. In `versioned-state` we've created
255−created a union and just added our `state-0` type. We've added an extra `card`
258+a union and just added our `state-0` type. We've added an extra `card` arm as
256−arm as well, just so we can use `card` as a type, rather than the unweildy
259+well, just so we can use `card` as a type, rather than the unweildy
257`card:agent:gall`.
260`card:agent:gall`.
259After that core, we have the usual `agent:dbug` call, and then we have this:
262After that core, we have the usual `agent:dbug` call, and then we have this:
@@ -307,13 +310,11 @@ Finally, we have `on-load`:
309It takes in the old state in a `vase`, then unpacks it to the `versioned-state`
312It takes in the old state in a `vase`, then unpacks it to the `versioned-state`
310−type we defined earlier in our state definition core. We then test its head for
313+type we defined earlier. We test its head for the version, and load it back into
311−the version, and load it back into the state of our agent if it matches. This
314+the state of our agent if it matches. This test is a bit redundant at this stage
312−test is a bit redundant at this stage since we only have one state version, but
313−you'll soon see the purpose of it.
315+since we only have one state version, but you'll soon see the purpose of it.
315−You can save it as `app/lifecycle.hoon` in the `%base` desk and `|commit %base`.
317+You can save it as `/app/lifecycle.hoon` in the `%base` desk and `|commit %base`. Then, run `|rein %base [& %lifecycle]` to start it.
316−Then, run `|rein %base [& %lifecycle]` to start it.
318Let's try inspecting our state with `dbug`:
319Let's try inspecting our state with `dbug`:
@@ -331,7 +332,9 @@ Let's try inspecting our state with `dbug`:
334−Next, we're going to modify our agent and change the structure of the state so we can test out the upgrade process. Here's a modified version, which you can save in `app/lifecycle.hoon` and `|commit %base`:
335+Next, we're going to modify our agent and change the structure of the state so
336+we can test out the upgrade process. Here's a modified version, which you can
337+again save in `/app/lifecycle.hoon` and `|commit %base`:
337/+ default-agent, dbug
340/+ default-agent, dbug
@@ -425,11 +428,11 @@ this new version of the agent, it would be, so we still need to update it.
426We've updated the wuthep expression with a new case that handles our new state
429We've updated the wuthep expression with a new case that handles our new state
427type, and for the old state type we've added a function that converts it to the
430type, and for the old state type we've added a function that converts it to the
428−new type - in this case by just duplicating `val` and changing the head-tag from
431+new type - in this case by duplicating `val` and changing the head-tag from `%0`
429−`%0` to `%1`. This is an extremely simple state type transition function, but it
432+to `%1`. This is an extremely simple state type transition function - it would
430−would likely be more complicated for an agent with real functionality.
433+likely be more complicated for an agent with real functionality.
432−Let's now use `dbug` to check out state has successfully been updated to the new
435+Let's now use `dbug` to check our state has successfully been updated to the new
@@ -440,8 +443,6 @@ type:
441## Summary
444## Summary
443−The key takeways are:
445- The app lifecycle rougly consists of initialization, state export, upgrade,
446- The app lifecycle rougly consists of initialization, state export, upgrade,
446 state import and state version transition.
447 state import and state version transition.
447- This is managed by three arms: `on-init`, `on-save` and `on-load`.
448- This is managed by three arms: `on-init`, `on-save` and `on-load`.
@@ -462,12 +463,12 @@ The key takeways are:
463## Exercises
464## Exercises
465−- Run through the [Example](#example) yourself on a fake ship if you've not done
466+- Run through the [example](#example) yourself on a fake ship if you've not done
466 so already.
467 so already.
467- Have a look at the [`vase` entry in the type
468- Have a look at the [`vase` entry in the type
468 reference](/docs/userspace/gall-guide/types#vase).
469 reference](/docs/userspace/gall-guide/types#vase).
469- Have a look at the [`quip` entry in the type
470- Have a look at the [`quip` entry in the type
470 reference](/docs/userspace/gall-guide/types#quip).
471 reference](/docs/userspace/gall-guide/types#quip).
471−- Try modifying the second version of the agent in the [Example](#example)
472+- Try modifying the second version of the agent in the [example](#example)
472 section, adding a third state version. Include functions in the wuthep
473 section, adding a third state version. Include functions in the wuthep
473 expression in `on-load` to convert old versions to your new state type.
474 expression in `on-load` to convert old versions to your new state type.