@@ -4,25 +4,25 @@ weight = 20
44template = "doc.html"
55+++
66
7−In the last lesson we look at some useful bits and pieces that are used as
8−boilerplate in most agents. Now we're going to get into the guts of how agents
9−work, and start looking at what the agent arms do. The first thing we'll look at
10−is the agent's state, and the three arms for managing it: `on-init`, `on-save`,
11−and `on-load`. These arms handle what we call an agent's "lifecycle".
7+In the last lesson we look at a couple of useful things used as boilerplate in
8+most agents. Now we're going to get into the guts of how agents work, and start
9+looking at what the agent arms do. The first thing we'll look at is the agent's
10+state, and the three arms for managing it: `on-init`, `on-save`, and `on-load`.
11+These arms handle what we call an agent's "lifecycle".
1212
1313## Lifecycle
1414
1515An 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
17−ever called - its purpose is just to initialize the agent. The `on-init` arm
18−might be very simple and just set an initial value for the state, or even do
19−nothing at all and return the agent core exactly as-is. It may also be more
20−complicated, and perform some [scries](/docs/arvo/concepts/scry) to obtain extra
21−data or check that another agent is also installed. It might send off some
22−`card`s to other agents or vanes to do things like load data in to the
23−`%settings-store` agent, bind an Eyre endpoint, or anything else. It all depends
24−on the needs of a particular agent. If `on-init` fails for whatever reason, the
25−agent installation will fail and be aborted.
16+agent's `on-init` arm is called. This is the _only_ time `on-init` is ever
17+called - its purpose is just to initialize the agent. The `on-init` arm might be
18+very simple and just set an initial value for the state, or even do nothing at
19+all and return the agent core exactly as-is. It may also be more complicated,
20+and perform some [scries](/docs/arvo/concepts/scry) to obtain extra data or
21+check that another agent is also installed. It might send off some `card`s to
22+other agents or vanes to do things like load data in to the `%settings-store`
23+agent, bind an Eyre endpoint, or anything else. It all depends on the needs of
24+your particular application. If `on-init` fails for whatever reason, the agent
25+installation will fail and be aborted.
2626
2727Once initialized, an agent will just go on doing its thing - processing events,
2828updating 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
3333- The agent's `on-save` arm is called, which packs the agent's state in a `vase`
3434 and exports it.
3535- 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
37− `on-load` arm of the newly built agent. The `on-load` arm will process it,
38− convert it to the new version of the state if necessary, and load it back
39− into the state of the agent.
36+- The previously exported `vase` is passed to the `on-load` arm of the newly
37+ built agent. The `on-load` arm will process it, convert it to the new version
38+ of the state if necessary, and load it back into the state of the agent.
4039
4140A `vase` is just a cell of `[type-of-the-noun the-noun]` - it's used extensively
4241for 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:
6362
6463However, when you update your agent as described in the [Lifecycle](#lifecycle)
6564section, 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
67−the situation. Over time, as you make further changes to the type of the state,
68−this becomes quite complicated.
65+`on-load` might find old versions of the state in the `vase` it receives. Over
66+time, as you make further changes to the type of the state, this becomes quite
67+complicated.
6968
7069For example, if you were creating an agent for a To-Do task management app, your
7170tasks 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
8685first version of the state would typically be called `state-0`, and its head
8786would be tagged with `%0`. Then, when you change the state's type in an update,
8887you'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
90−state versions, you'd also define a structure called `versioned-state`, which
91−just contains a union of all the possible states. This way, the vase `on-load`
92−receives can be unpacked to a `versioned-state` type, and then a
88+would then be `state-2`, and so on.
89+
90+In addition to each of those individual state versions, you'd also define a
91+structure called `versioned-state`, which just contains a union of all the
92+possible states. This way, the vase `on-load` receives can be unpacked to a
93+`versioned-state` type, and then a
9394[wuthep](/docs/hoon/reference/rune/wut#--wuthep) (`?-`) expression can switch on
9495the head (`%0`, `%1`, `%2`, etc) and process each one appropriately.
9596
@@ -141,11 +142,13 @@ then on the arms of our agent will just modify it.
141142## State management arms
142143
143144We'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:
145146
146147### `on-init`
147148
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.
149+This arm takes no argument, and produces a `(quip card _this)`. It's called
150+exactly once, when the agent is first installed. Its purpose is to initialize
151+the agent.
149152
150153`(quip a b)` is equivalent to `[(list a) b]`, see the [types
151154reference](/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
177180resumed or reinstalled.
178181
179182The 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)`.
181184
182185### `on-load`
183186
184187This 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
186189you'd have used a [versioned state](#versioned-state-type) as described above,
187190so this arm would test which state version the imported data has, convert data
188191from an old version to the new version if necessary, and load it into the
@@ -251,9 +254,9 @@ state core:
251254
252255In `state-0` we've defined the structure of our state, which is just a `@ud`.
253256We'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
255−created a union and just added our `state-0` type. We've added an extra `card`
256−arm as well, just so we can use `card` as a type, rather than the unweildy
257+`on-load` can easily test the state version. In `versioned-state` we've created
258+a union and just added our `state-0` type. We've added an extra `card` arm as
259+well, just so we can use `card` as a type, rather than the unweildy
257260`card:agent:gall`.
258261
259262After that core, we have the usual `agent:dbug` call, and then we have this:
@@ -307,13 +310,11 @@ Finally, we have `on-load`:
307310```
308311
309312It 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
311−the version, and load it back into the state of our agent if it matches. This
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.
313+type we defined earlier. We test its head for the version, and load it back into
314+the state of our agent if it matches. This test is a bit redundant at this stage
315+since we only have one state version, but you'll soon see the purpose of it.
314316
315−You can save it as `app/lifecycle.hoon` in the `%base` desk and `|commit %base`.
316−Then, run `|rein %base [& %lifecycle]` to start it.
317+You can save it as `/app/lifecycle.hoon` in the `%base` desk and `|commit %base`. Then, run `|rein %base [& %lifecycle]` to start it.
317318
318319Let's try inspecting our state with `dbug`:
319320
@@ -331,7 +332,9 @@ Let's try inspecting our state with `dbug`:
331332>=
332333```
333334
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`:
335338
336339```hoon
337340/+ default-agent, dbug
@@ -425,11 +428,11 @@ this new version of the agent, it would be, so we still need to update it.
425428
426429We've updated the wuthep expression with a new case that handles our new state
427430type, 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
429−`%0` to `%1`. This is an extremely simple state type transition function, but it
430−would likely be more complicated for an agent with real functionality.
431+new type - in this case by duplicating `val` and changing the head-tag from `%0`
432+to `%1`. This is an extremely simple state type transition function - it would
433+likely be more complicated for an agent with real functionality.
431434
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
433436type:
434437
435438```
@@ -440,8 +443,6 @@ type:
440443
441444## Summary
442445
443−The key takeways are:
444−
445446- The app lifecycle rougly consists of initialization, state export, upgrade,
446447 state import and state version transition.
447448- This is managed by three arms: `on-init`, `on-save` and `on-load`.
@@ -462,12 +463,12 @@ The key takeways are:
462463
463464## Exercises
464465
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
466467 so already.
467468- Have a look at the [`vase` entry in the type
468469 reference](/docs/userspace/gall-guide/types#vase).
469470- Have a look at the [`quip` entry in the type
470471 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)
472473 section, adding a third state version. Include functions in the wuthep
473474 expression in `on-load` to convert old versions to your new state type.