on f3ca521 help sign in
601 lines 20 kB history raw
1+++
2title = "1.9 Generators"
3weight = 19
4template = "doc.html"
5aliases = ["/docs/learn/hoon/hoon-tutorial/generators/"]
6+++
8Generators are the most straightforward way to write programs for Urbit. They
9are used for doing computations that do not require persistence: they take an
10input and produce an output, then disappear. Generators might make sense for
11listing directory contents, or running unit tests, or fetching the contents of a
12URL.
14There are four kinds of generators: naked, `%say`, `%get`, and `%ask`.
16### Naked Generators
18A naked generator is simply a `gate`; that is, it is an anonymous function that
19takes a `sample` (argument) and produces a noun. All you need to do is write a
20`gate` and put it into a file in the `/gen` directory. Let's take a look at a
21very simple one:
23```hoon
24|= a=*
26```
28This generator takes one argument of any noun and produces it without any
29changes. If we put this into a file named `echo.hoon` in the `/gen` directory,
30we can run it from the [Dojo](@/operations.md):
32```
33> +echo 42
3442
35```
37This command just passes in 42 and gets 42 back. But what about when we pass in
38`"asdf"`?
40```
41> +echo "asdf"
42[97 115 100 102 0]
43```
45We just get a pile of numbers back in the form of a raw `noun`. The numbers that
46compose that noun are called `atoms`, and how they are interpreted is a matter
47of what `aura` is being applied to them. For the purposes of this lesson, an
48`aura` tells the pretty-printer how to display an `atom`, but keep in mind that
49an `aura` is type metadata and does other things, too.
51In the example above, we didn't specify an `aura`, leaving the printer to fend
52for itself. `"asdf"` is a `tape`, a type that is simply a `list` of `cords`. A
53`cord` is itself an atom represented as a string of UTF-8 characters. When used
54as part of a `tape`, the `cord` is only a single character. So each atom in the
55`[97 115 100 102 0]` output corresponds to a component of the `tape`: 97 is
56`a`, 115 is `s`, 100 is `d`, 102 is `f`, and `0` is the "null" that every list
57is terminated with.
59We can tell the Dojo to cast -- apply a specific type to -- the output of our
60generator to see something more familiar:
62```
63> _tape +echo "asdf"
64"asdf"
65```
67Now let's create a generator with two arguments instead of one. Save the code
68below as `add.hoon` in the `/gen` directory.
70```hoon
71|= [a=@ud b=@ud]
72(add a b)
73```
75Now, run the generator:
77```
78> +add [3 4]
80```
82You may notice our generator takes a cell containing two `@ud`. This is
83actually one of the limitations of naked generators. We can only pass one
84argument. We can get around this, of course, by passing a cell, but it's less
85than ideal. Also, a naked generator cannot be called without an argument.
86This brings us to `%say` generators.
88### `%say` Generators
90We use `%say` generators when we want to provide something else in Arvo, the
91Urbit operating system, with metadata about the generator's output. This is
92useful when a generator is needed to pipe data to another program, a frequent
93occurrence.
95To that end, `%say` generators use `mark`s to make it clear, to other Arvo
96computations, exactly what kind of data their output is. A `mark` is akin
97to a MIME type on the Arvo level. A `mark` describes the data in
98some way, indicating that it's an `%atom`, or that it's a standard such as
99`%json`, or even that it's an application-specific data structure like
100`%talk-command`. `mark`s are not specific to `%say` generators; whenever data
101moves between programs in Arvo, that data is marked.
103So, more formally, a `%say` generator is a `cell`. The head of that cell is the
104`%say` tag, and the tail is a `gate` that produces a `cask` -- a pair of the
105output data and the `mark` describing that data.
107Below is an example of a `%say` generator. Save it to `add.hoon` in the `/gen`
108directory.
110```hoon
111:- %say
112|= *
113:- %noun
114(add 40 2)
115```
117Now run the generator as below:
119```
120> +add
12142
122```
124Notice that we used no argument, something that is possible with `%say`
125generators but impossible with naked generators. We'll explain that in a moment.
126For now, let's focus on the code that is necessary to make something a `%say`
127generator.
129```hoon
130:- %say
131```
133Recall that the rune `:-` produces a cell, with the first following expression
134as its head and the second following expression as its tail.
136The expression above creates a cell with `%say` as the head. The tail is
137the `|= *` expression on the line that follows.
139```hoon
140|= *
141:- %noun
142(add 40 2)
143```
145`|= *` constructs a gate that takes a noun. This gate will itself produce a
146`cask`, which is cell formed by the prepending `:-`. The head of that `cask` is
147`%noun` and the tail is the rest of the program, `(add 40 2)`. The tail of the
148`cask` will be our actual data produced by the body of the program: in this
149case, just adding 40 and 2 together.
151#### `%say` generators with arguments
153We can modify the boilerplate code to allow arguments to be passed into a `%say`
154generator, but in a way that gives us more power than we would have if we just
155used a naked generator.
157Naked generators are limited because they have no way of accessing data that
158exists in Arvo, such as the date and time or pieces of fresh entropy. In `%say`
159generators, however, we can access that kind of subject by identifying them
160in the gate's sample, which we only specified as `*` in the previous few
161examples. But we can do more with `%say` generators if we do more with that
162sample. Any valid sample will follow this 3-tuple scheme:
164`[[now, eny, beak] [list of unnamed arguments] [list of named arguments]]`
166This entire structure is a noun, which is why `*` is a valid sample if we
167wish to not use any of the information here in a generator. But let's look at
168each of these three elements, piece by piece.
170The **first part** of the above 3-tuple is a noun that is composed of three
171atoms:
173`now` is the current time.
174`eny` is 512 bits of entropy for seeding random number generators.
175`beak` contains the current ship, desk, and case.
177You can access each of those pieces of data by typing their names into the Dojo.
178But in a generator, we need to put faces (variable names) onto that data so we
179can easily use it in the program. We can do so like this:
181```hoon
182|= [[now=@da eny=@uvJ bec=beak] ~ ~]
183```
185Any of those pieces of data could be omitted by replacing part of the noun with
186`*` rather than giving them faces. For example, `[now=@da * bec=beak]` if we
187didn't want `eny`, or `[* * bec=beak]` if we only wanted `beak`.
189The **second part** of the sample is a `list` of arguments that _must_ be passed
190to the generator as it is run. Because it's a `list`, this element needs to be
191terminated with a `~`. In the example above, we used `~`, the empty list, to
192represent this second part. But that would mean we don't want to use any new
193faces for our generator. So a sample where we want to declare a single required
194argument would look like this:
196```hoon
197|= [* [n=@ud ~] ~]
198```
200In the above code, we use a `*` to ignore any of the information that would go
201in the first part of the sample, somewhat similar to how we use a `~` to say
202"nothing here" for the second or third parts of the sample.
204But to use both parts together, our gate and sample would look like this:
206```hoon
207|= [[now=@da eny=@uvJ bec=beak] [n=@ud ~] ~]
208```
210The **third part** of the sample is a list of _optional_ arguments. These
211arguments may be passed to the program as it's being run, but the program doesn't
212require them. The syntax for the third part is just like the syntax for the
213second part, besides its position:
215```hoon
216|= [* ~ [bet=@ud ~]]
217```
219Let's look at an example that uses all three parts. Save the code below in a
220file called `dice.hoon` in your `/gen` directory.
222```hoon
223:- %say
224|= [[now=@da eny=@uvJ bec=beak] [n=@ud ~] [bet=@ud ~]]
225:- %noun
226[(~(rad og eny) n) bet]
227```
229This is a very simple dice program with an optional betting functionality.
230In the code, our sample specifies faces on all of the Arvo data, meaning that we
231can easily access them. We also require the argument `[n=@ud ~]`, and allow the
232_optional_ argument `[bet=@ud ~]`.
234But there's something new: `(~(rad og eny) n)`. This code pulls the `rad` arm
235out of the `og` core with the subject of `eny`. Recall that `eny` is our entropy
236value, so this is used to seed the generator. The `rad` arm will give us a
237pseudorandom number between 0 and `n`. Then we form a cell with the result and
238`bet`, the optional named argument specified previously.
240We can run this generator like so:
242```
243> +dice 6, =bet 2
244[4 2]
246> +dice 6
247[5 0]
249> +dice 6
250[2 0]
252> +dice 6, =bet 200
253[0 200]
255> +dice
256nest-fail
257```
259We get a different value from the same generator between runs, something that
260isn't possible with a naked generator. Another novelty is the ability to choose
261to not use the second argument.
263##### Arguments without a cell
265Also unlike a naked generator, we don't need to put our arguments together into
266a cell. Swap the code below into your `add.hoon` file.
268```hoon
269:- %say
270|= [* [a=@ud b=@ud ~] ~]
271:- %noun
272(add a b)
273```
275In the above code we're again creating a `%say` generator, but the sample of our
276gate is a little different than before. We are using `*` for the first part of
277the sample, because we don't use any Arvo data in this program. We are using a
278`~` for the third part, because we aren't allowing optional arguments -- the
279list of optional arguments is _empty_.
281Run a command like the one below in the Dojo. Notice that you can use two
282arguments that aren't in the cells.
284```
285> +add 40 2
28642
287```
289### `%ask` generators
291We use the `%ask` generator when we want to create an interactive program
292that prompts for inputs as it runs, rather than expecting arguments to be passed
293in at the time of initiation.
295Code-wise, two things are needed to create a useful `%ask` generator.
297The first is **`sole-result`**, which lives in `/sur/sole.hoon`. It's a
298mold-builder that's required by any part of Arvo, such as the Dojo, that wants
299to understand the output of the `%ask` generator. Because it's the only type of
300`%ask` data that is intelligible to the system, the ultimate output of an `%ask`
301generator must be `sole-result`. You can think of it as a function that takes
302molds and digests them into a type for the operating system. The simplest way to
303use `sole-result` is by calling the `produce` gate from `/lib/generators.hoon`.
305The second is **`prompt`**, a gate that lives in `lib/generators.hoon`. `prompt`
306allows you to get information back from the user, which is essential to the
307typical desired operation of an `%ask` generator.
309Since this isn't easy to digest in the abstract, we'll use an example generator
310to explain how this kind of generator works.
312##### `%ask` example
314The code below is an `%ask` generator that checks if the user inputs "blue" when
315prompted. Save it as `axe.hoon` in `/gen`.
317```hoon
318/- sole
319/+ generators
320=, [sole generators]
321:- %ask
322|= *
323^- (sole-result (cask tang))
324%+ print leaf+"What is your favorite color?"
325%+ prompt [%& %prompt "color: "]
326|= t=tape
327%+ produce %tang
328?: =(t "blue")
329 :~ leaf+"Oh. Thank you very much."
330 leaf+"Right. Off you go then."
331 ==
332:~ leaf+"Aaaaagh!"
333 leaf+"Into the Gorge of Eternal Peril with you!"
334==
335```
337Run the generator from the Dojo:
339```
340> +axe
341What is your favorite color?
342: color:
343```
345Something new happened. Instead of simply returning something, your Dojo's
346prompt changed from `~your-urbit:dojo>` to `~your-urbit:dojo: color:`, and
347now expects additional input. Let's give it an input:
349```
350: color: red
351Into the Gorge of Eternal Peril with you!
352Aaaaagh!
353```
355Let's go over what exactly is happening in this code.
357```hoon
358/- sole
359/+ generators
360=, [sole generators]
361```
363Here we bring in some of the types we are going to need from `/sur/sole` and
364gates we will use from `/lib/generators`. We use some special runes for this.
366`/-` is a Ford rune used to import types from `sur`.
368`/+` is a Ford rune used to import libraries from `lib`.
370`=,` is a rune that allows us to expose a namespace. We do this to avoid having
371to write `sole-result:sole` instead of `sole-result` or `print:generators`
372instead of `print`.
374```hoon
375:- %ask
376|= *
377```
379This code might be familiar. Just as with their `%say` cousins, `%ask`
380generators need to produce a `cell`, the head of which specifies what kind of
381generator we are running.
383With `|= *`, we create a gate and ignore the standard arguments we are given,
384because we're not using them.
386```hoon
387^- (sole-result (cask tang))
388```
390`%ask` generators need to have the second half of the cell be a gate that
391produces a `sole-result`, one that in this case contains a `cask` of `tang`.
392We use the `^-` rune to constrain the generator's output to such a
393`sole-result`.
395A `cask` is a pair of a `mark` name and a noun. Recall that a `mark` can be
396thought of as an Arvo-level MIME type for data.
398A `tang` is a `list` of `tank`, and a `tank` is a structure for printing data.
399There are three types of `tank`: `leaf`, `palm`, and `rose`. A `leaf` is for
400printing a single noun, a `rose` is for printing rows of data, and a `palm` is
401for printing backstep-indented lists.
403```hoon
404%+ print leaf+"What is your favorite color?"
405%+ prompt [%& %prompt "color: "]
406|= t=tape
407%+ produce %tang
408```
410Because we imported `generators`, we can access its contained gates, three of
411which we use in `axe.hoon`: `print`, `prompt`, and `produce`.
413**`print`** is used for printing a `tank` to the console.
415In our example, `%+` is the rune to call a gate, and our gate `print` takes one
416argument which is a `tank` to print. The `+` here is syntactic sugar for
417`[leaf "What is your favorite color?"]` that just makes it easier to write.
419**`prompt`** is used to construct a prompt for the user to provide input.
420It takes a single argument that is a tuple. Most `%ask` generators will want to
421use the `prompt` gate.
423The first element of the `prompt` sample is a flag that indicates whether what
424the user typed should be echoed out to them or hidden. `%&` will produce echoed
425output and `%|` will hide the output (for use in passwords or other secret
426text).
428The second element of the `prompt` sample is intended to be information for use
429in creating autocomplete options for the prompt. This functionality is not yet
430implemented.
432The third element of the `prompt` sample is the `tape` that we would like to
433use to prompt the user. In the case of our example, we use `"color: "`.
435**`produce`** is used to construct the output of the generator. In our example,
436we produce a `tang`.
439```hoon
440|= t=tape
441```
443Our gate here takes a `tape` that was produced by `prompt`. If we needed
444another type of data we could use `parse` to obtain it.
446The rest of this generator should be intelligible to those with Hoon knowledge
447at this point.
449One quirk that you should be aware of, though, is that `tang` prints in reverse
450order from how it is created. The reason for this is that `tang` was originally
451created to display stack trace information, which should be produced in reverse
452order. This leads to an annoyance: we either have to specify our messages
453backwards or construct them in the order we want and then `flop` the `list`.
454This is a [known issue](https://github.com/urbit/arvo/issues/840) to be resolved.
456### `%get` generators
458`%get` generators are used for making HTTP requests through `eyre`. For this
459section we'll assume knowledge of generators covered in previous sections, so
460we can jump in with an example:
462```hoon
463/- sole
464/+ generators
465=, generators
466:- %get
467|= [* [url=tape ~] ~]
468^- (sole-request:sole (cask json))
469%+ curl (scan url auri:de-purl:html)
470|= hit/httr:eyre
471?~ r.hit !!
472=/ my-json (de-json:html q.u.r.hit)
473?~ my-json !!
474=, dejs:format
475%+ produce %json
476%. %title
477%~ got by
478%- (om same)
479u.my-json
480```
482The above generator was written with the
483[Studio Ghibli API](https://ghibliapi.herokuapp.com/). It uses this API to
484print the title of a Studio Ghibli film.
486```hoon
487/- sole
488/+ generators
489=, generators
490```
492Here we import types from `/sur/sole.hoon` and gates from
493`/lib/generators.hoon`. We also use `=,` to expose the namespace of
494`generators` that we just imported.
496```hoon
497:- %get
498|= [* [url=tape ~] ~]
499```
501Like with `%say` and `%ask` generators, we must include the "boilerplate":
502code that produces a generator-specific `cell`. The head of that cell is `%get`,
503and the tail is a gate that takes a `tape` of the URL that we are trying
504to access.
506```hoon
507^- (sole-request:sole (cask json))
508```
510Here we make sure our gate is producing a `sole-request` which contains a `cask`
511of the type `json`.
514```hoon
515%+ curl (scan url auri:de-purl:html)
516|= hit/httr:eyre
517```
519Here we can see the call to `curl`, the sample of which will parse our url and
520make sure it's in the proper format.
522The second part of the `curl` call is the `gate` that will get called with the
523result provided by the actual HTTP request.
525```hoon
526?~ r.hit !!
527```
529Here we verify that the `r` face of our response is not null before
530continuing. If it is null, the generator will crash, as there is nothing left to
531do.
533```hoon
534=/ my-json (de-json:html q.u.r.hit)
535```
537Here we verify that data was returned to us in the `json` Hoon type.
538`q.u.r.hit` is the actual data we care about as a `cord`. `hit` is actually an
539entire response object, including headers, which we don't care about for this
540example. `de-json` out of `html` will parse the `cord` and produce a `json` type
541for us that we can then use to inspect the data.
543```hoon
544?~ my-json !!
545=, dejs:format
546```
548After parsing the JSON structure we want to make sure we got back a valid `json`
549type. To do that, we test for null and crash if that test passes. If it doesn't
550pass, we use `=,` on the `dejs:format` core so that we can more easily pull arms
551out of it.
553```hoon
554%+ produce %json
555%. %title
556%~ got by
557```
559Now we come to our `produce` call. Recall that `produce` is used in all the
560non-naked generators to output the final result. Here we want to produce a
561`cask` of `json`, so we use the mark `%json` to indicate that.
563Next we have what may be an unfamiliar rune: `%.`. This rune is simply `%-` with
564the argument order reversed. `%title` is the argument passed to the next gate
565that is called. `by` is the core that is the `map` engine. A Hoon `map` is a
566key-value pair structure, sometimes referred to as a dictionary in other
567languages. `got` is the arm in `by` that will produce a gate that we can call to
568access members of a given `map`.
570> The idiomatic way to write Hoon is to have the
571 "heaviest" code paths lowest in the program, which is why `%.`
572 is preferred in this case over `%-`. Use of this style is recommended, as it
573 results in code that is much easier to read.
575At the moment, however, we do not have a `map`, but instead have a `json` type.
577```hoon
578%- (om same)
579u.my-json
580```
582Here we use `om`, which is an arm in `dejs:format` that will take a `json` and
583return a `map`. `same` is a wet gate from the standard library that takes an
584argument of any type and returns it as that type. We could use any other gate to
585modify the data if that were useful for the particular data we are going to be
586processing, but here we want the data exactly as it is.
588Since `my-json` is a `unit` of `json`, the actual data we are looking for is in
589`u.my-json`. We take this and give it to the gate produced by the call to `om`.
590This gate is going to transform our data by taking in the `json` structure and
591producing a `map`. Then we can look up keys in this map using `got` from
592the `by` core.
594`%.` performs the same function as `%-`, the rune used to call a function,
595except with the arguments reversed so that the function is last and
596the arguments of that function come first. It's used here to keep with the
597principle that heaviest, or most complex, expressions should go to the bottom.
599The key we are looking up in our new `map` we've created from `my-json` is
600`%title`, which is an element in the original source, to produce the final
601result of the generator.