Appendix: Types

This document explains a few of the types commonly used in Gall agents. In addition to these, the Data Types section of the Gall vane documentation is a useful reference. In particular, the whole agent subsection, as well as bowl, boat, and bitt.

vase

Vases are used to encapsulate dynamically typed data - they let typed data be moved around in contexts where you can't know the type ahead of time, and therefore can't have a static type.

Vases are used extensively - almost all data your agent will send and received is wrapped in a vase.

A vase is just a cell with data in the tail and the type of the data in the head. Its formal definition is:

+$ vase [p=type q=*]

Here's what it looks like if we bunt a vase in the dojo:

> *vase
[#t/* q=0]

There are two simple runes used to create and unpack vases. We'll look at each of these next.

Create a vase

The zapgar rune (!>) takes a single argument of any noun, and wraps it in a vase. For example, in the dojo:

> !>([1 2 3])
[#t/[@ud @ud @ud] q=[1 2 3]]
> !>('foo')
[#t/@t q=7.303.014]
> !>([[0xdead 0xb33f] 'foo'])
[#t/[[@ux @ux] @t] q=[[57.005 45.887] 7.303.014]]
> !>(foo='bar')
[#t/foo=@t q=7.496.034]

You would typically use !> as part of a cage when you're constructing a card like a poke or a %fact gift to be sent off.

Extract data from vase

The zapgal rune (!<) takes two arguments: A mold specifying the type to try and extract the data as, and the vase to be extracted.

Let's look at an example in the dojo. First, let's create a vase of [@t @ux @ud]:

> =myvase !>(['foo' 0xabcd 123])
> myvase
[#t/[@t @ux @ud] q=[7.303.014 43.981 123]]

Next, let's try extracting our vase:

> !< [@t @ux @ud] myvase
['foo' 0xabcd 123]

Now let's try asking for a @p rather than @t:

> !< [@p @ux @ud] myvase
-need.@p
-have.@t
nest-fail

As you can see, it will crash if the type does not nest. Note that rather than using !<, you can also just clam the tail of the vase like:

> ((trel @t @ux @ud) +.myvase)
[p='foo' q=0xabcd r=123]

The only problem is that you can't tell if the auras were wrong:

> ((trel @p @ud @ux) +.myvase)
[p=~sibtel-tallyd q=43.981 r=0x7b]

You'd typically use !< on the data in cards that come in from other ships, agents, etc.

mark

The mark type is just a @tas like %foo, and specifies the Clay filetype of some data. The mark corresponds to a mark file in the /mar directory, so a mark of %foo corresponds to /mar/foo/hoon. Mark files are used for saving data in Clay, validating data sent between agents or over the network, and converting between different data types. For more information about mark files, you can refer to the Marks section of the Clay documentation.

cage

A cage is a cell of a mark and a vase, like [%foo !>('bar')]. The data in the vase should match the data type of the specified mark.

Most data an agent sends will be in a cage, and most data it receives will arrive in a cage. The mark may be used to validate or convert the data in the vase, depending on the context.

quip

quip is a mold-builder. A (quip a b) is equivalent to [(list a) b], it's just a more convenient way to specify it. Most arms of an agent return a (quip card _this), which is a list of effects and a new state.

path

The path type is formally defined as:

+$ path (list knot)

A knot is a @ta text atom (see the Strings guide for details), so a path is just a list of text. Rather than having to write [~.foo ~.bar ~.baz ~] though, it has its own syntax which looks like /foo/bar/baz.

A path is similar to a filesystem path in Unix, giving data a location in a nested hierarchy. In Arvo though, they're not only used for files, but are a more general type used for several different purposes. Its elements have no inherent significance, it depends on the context. In a Gall agent, a path is most commonly a subscription path - you might subscribe for updates to /foo/bar on another agent, or another agent might subscribe to /baz on your agent.

A path might just be a series of fixed @ta like /foo/bar, but some elements might also be variable and include encoded atoms, or some other datum. For example, you might like to include a date in the path like /updates/~2021.10.31..07.24.27..db68. Other agents might create the path by doing something like:

/update/(scot %da now.bowl)

Then, when you get a subscription request, you might do something like:

?+ path !!
[%updates @ ~]
=/ date=@da (slav %da i.t.path)
...(rest of code)...

See the Encoding in text and Decoding from text sections of the Strings guide for more information on dealing with atoms encoded in strings.

Aside from using function calls when constructing a path as demonstrated above, you can also insert text you're previously stored with =/ or what have you, simply by enclosing them in brackets. For example, in the dojo:

> =const ~.bar
> `path`/foo/[const]/baz
/foo/bar/baz

wire

The type of a wire is formally defined as:

+$ wire path

So, a wire is just a path, type-wise they're exactly the same. The reason there's a separate wire type is just to differentiate their purpose. A wire is a path for responses to requests an agent initiates. If you subscribe to the path /some/path on another agent, you also specify /some/wire. Then, when that agent sends out updates to subscribers of /some/path, your agent receives them on /some/wire.

More formally, wires are used by Arvo to represent an event cause, and therefore return path, in a call stack called a duct. Inter-vane communications happen over ducts as moves, and Gall converts the cards produced by agents into such moves behind the scenes. A detailed understanding of this system is not necessary to write Gall agents, but if you're interested it's comprehensively documented in the Arvo overview and move trace tutorial.

For agents, the wire is specified in the second argument of a %pass card. It's used for anything you can %pass, such as %pokes, %watches, and %arvo notes. For example:

[%pass /this/is/wire %agent [~zod %foobar] %watch /this/is/path]
::
[%pass /this/is/wire %agent [~zod %foobar] %poke %foo !>('hello')]
::
[%pass /this/is/wire %arvo %b %wait (add now.bowl ~m1)]

The on-agent and on-arvo arms of the agent core include a wire in their respective sample. Responses from agents come in to the former, and responses from vanes come in to the latter.

Edit this page on GitHub

Last modified October 8, 2023