5 title/'Using generators with apps'
11Up until now we've poked apps directly. This requires the user to
12specify the mark, and it requires the app to accept the arguments in a
13way that's convenient for users to input. This is the "plumbing" way to
14interact with apps. Generators are the "porcelain" layer. This is why
15when you run a command like `+ls` or `|merge`, there are no marks in
18We've used generators before, back in [Basic
19Operation](/docs/using/admin). At that point, we just used the
20generators to produce values -- we didn't pipe their results into apps.
21In the dojo cast, the role of a generator is to take a list of arguments
22and produce a value, which is often, though not always, piped into an
23app. Generators are pure, stateless functions, and they cannot send
26`+ls` and `+cat` are commonly-used generators that produce a directory
27listing and print out a file, respectively. These are useful in
28themselves, so we generally don't pipe the results into an app.
30Another generator is `+hood/merge`. Try running `+hood/merge` with the
31normal arguments for `|merge`:
34> +hood/merge %home our %they
35[syd=%home her=~zod sud=%they cas=[%da p=~2018.10.4..21.11.49..901c] gem=%auto]
37> +hood/merge %home our %they, =gem %this
38[syd=%home her=~zod sud=%they cas=[%da p=~2018.10.4..21.12.11..ee32] gem=%this]
41This didn't run any merge, it just constructed the command that, if sent
42to `:hood`, would run it. Note first that, even though it's not printed,
43this value has mark `kiln-merge`. Also, the merge strategy `gem=%auto`
44was added automatically. Optional arguments are straightforward with
47In general, generators are prepended by either `+` or `|`. The general
48form is `+generator`, but often generators are created specifically to
49work with a single app, they're usually placed in a subdirectory of gen
50and run with `|`. Thus, if `my-generator` generator is made for use with
51`:my-app`, then `my-generator` is put in
52`/gen/my-app/my-generator.hoon`, and can be run directly with
53`+my-app/my-generator <args>`. If you want to pipe the result into
54`:my-app`, then run `:my-app +my-app/my-generator <args>`. Because this
55pattern is so common, this can be abbreviated to
56`:my-app|my-generator <args>`. Because most built-in commands are
57generators for the `:hood` app, `:hood|generator <args>` can be
58shortened to `|generator <args>`.
60Let's write a generator for a modified version of `:pong` from
61the chapter on [Network Messages](/docs/arvo/system/network). Recall
62that `:pong` takes an urbit address, which is of mark `urbit`,
63and sends that urbit the message `'howdy'`. First--without a
64generator--let's make `:ping` that does the same, except that
65it lets the user optionally specify the message as well.
67We'll need a new mark for our arguments. Let's call it
70> For app-specific marks, it's good style to prefix the name of the mark with the name of the app. Since many apps have several such marks, subdirectories in `/mar` are rendered as `-`, so that `ping-message` is written in (/mar/ping/message.hoon).
74:::: /mar/ping/message/hoon
85The app can easily be modified to use this (`/app/ping.hoon`):
88:: Allows one ship to ping another with a string of text
94 +$ move [bone term wire *]
97|_ [bowl:gall state=%~]
100 |= [to=@p message=@t]
102 ^- [(list move) _+>.$]
104 :~ `move`[ost %poke /sending [to dap] %atom message]
111 ^- [(list move) _+>.$]
113 ~& [%receiving (@t arg)]
120Now we can run this with:
123~fintud-macrep:dojo> |start %ping
126~fintud-macrep:dojo> :ping &ping-message [~sampel-sipnym 'heyya']
130And on `~sampel-sipnym`, assuming it's running `:ping` as well,
131we see `[%receiving 'heyya']`.
133This is an annoying way to invoke the app, though. The `[]` are
134mandatory, and optional arguments are hard. Let's make a generator,
135`+send` to make everything nicer. Since it's specific to
136`:ping`, let's put it in `/gen/ping/send.hoon`:
140|= [^ [to=@p message=?(%~ [text=@t %~])] %~]
141[%ping-message to ?~(message 'howdy' text.message)]
144A couple of new things here. Firstly, `message=?(%~ [text=@t %~])] %~]`
145should be read as "the message is either null or a pair of text and
146null". Generator argument lists are always null-terminated, which makes
147it convenient to accept lists in tail position (which are particularly
148annoying without generators). `?(a b)` is the irregular form of
149`$?(a b)`, which is a union type. Thus, `?(a b)` means the type of
150anything that's in either `a` or `b`. Thus, in our case,
151`?(~ [text=@t ~])` is either null or a pair of text and null.
153This is run as follows:
156~fintud-macrep:dojo> :ping|send ~sampel-sipnym
159~fintud-macrep:dojo> :ping|send ~sampel-sipnym 'how do you do'
163Which causes `~sampel-sipnym` to print `[%receiving 'howdy']` and
164`[%receiving 'how do you do']`.
166(Note: `:ping|send ~sampel-sipnym` is short for `:ping +ping/send ~sampel-sipnym`.)
170- Create a generator for `:sum` from [State](../../state) so that you can run `:sum|add 5` to add numbers to it.
172- Create a generator for `:click` from [Web Apps](../../web-apps) so that you can run `:click|poke` to poke it.
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list