on 93773de help sign in
177 lines 6.1 kB history raw
1---
2next: true
3sort: 7
4title: Using Generators with Apps
5---
7Up until now we've poked apps directly. This requires the user
8to specify the mark, and it requires the app to accept the
9arguments in a way that's convenient for users to input. This is
10the "plumbing" way to interact with apps. Generators are the
11"porcelain" layer. This is why when you run a command like `+ls` or
12`|merge`, there are no marks in sight.
14We've used generators before, back in [Basic
15Operation](basic). At that point, we just used the generators
16to produce values -- we didn't pipe their results into apps. In
17the dojo cast, the role of a generator is to take a list of
18arguments and produce a value, which is often, though not always,
19piped into an app. Generators are pure, stateless functions, and
20they cannot send moves.
22`+ls` and `+cat` are commonly-used generators that produce a directory
23listing and print out a file, respectively. These are useful in
24themselves, so we generally don't pipe the results into an app.
26Another generator is `+hood/merge`. Try running `+hood/merge`
27with the normal arguments for `|merge`:
29```
30~fintud-macrep:dojo> +hood/merge %home our %they
31[syd=%home her=~fintud-macrep sud=%they gem=%auto]
32~fintud-macrep:dojo> +hood/merge %home our %they, =gem %this
33[syd=%home her=~fintud-macrep sud=%they gem=%auto]
34```
36This didn't run any merge, it just constructed the command that,
37if sent to `:hood`, would run it. Note first that, even though
38it's not printed, this value has mark `kiln-merge`. Also, the
39merge strategy `gem=%auto` was added automatically. Optional
40arguments are straightforward with generators.
42In general, generators are prepended by either `+` or `|`. The
43general form is `+generator`, but often generators are created
44specifically to work with a single app, they're usually placed in
45a subdirectory of gen and run with `|`. Thus, if `my-generator`
46generator is made for use with `:my-app`, then `my-generator` is
47put in `/gen/my-app/my-generator.hoon`, and can be run directly
48with `+my-app/my-generator <args>`. If you want to pipe the
49result into `:my-app`, then run `:my-app +my-app/my-generator
50<args>`. Because this pattern is so common, this can be
51abbreviated to `:my-app|my-generator <args>`. Because most
52built-in commands are generators for the `:hood` app,
53`:hood|generator <args>` can be shortened to `|generator <args>`.
55XXThe paragraph above is too long and confusing
57Let's write a generator for a modified version of `:pong` from
58the chapter on [Network Messages](network). Recall that `:pong`
59takes an urbit address, which is of mark `urbit`, and sends that
60urbit the message `'howdy'`. First--without a generator--let's
61make `:ping` that does the same, except that it lets the user
62optionally specify the message as well.
64We'll need a new mark for our arguments. Let's call it
65`ping-message`.
67> For app-specific marks, it's good style to prefix the name of
68> the mark with the name of the app. Since many apps have
69> several such marks, subdirectories in `/mar` are rendered as
70> `-`, so that `ping-message` is written in
71> (`/mar/ping/message.hoon`).
73```
74|_ [to=@p message=@t]
75++ grab
76 |%
77 ++ noun ,[@p @t]
78 --
79--
80```
82The app can easily be modified to use this (`/ape/ping.hoon`):
84```
85/? 314
86|%
87 ++ move ,[bone term wire *]
88--
89!:
90|_ [bowl state=~]
91::
92++ poke-ping-message
93 |= [to=@p message=@t]
94 ^- [(list move) _+>.$]
95 [[[ost %poke /sending [to dap] %atom message] ~] +>.$]
96::
97++ poke-atom
98 |= arg=@
99 ^- [(list move) _+>.$]
100 ~& [%receiving (,@t arg)]
101 [~ +>.$]
102::
103++ coup |=(* `+>)
104--
105```
107Now we can run this with:
109```
110~fintud-macrep:dojo> |start %ping
111>=
112~fintud-macrep:dojo> :ping &ping-message [~sampel-sipnym 'heyya']
113>=
114```
116And on `~sampel-sipnym`, assuming it's running `:ping` as well,
117we see `[%receiving 'heyya']`.
119This is an annoying way to invoke the app, though. The `[]` are
120mandatory, and optional arguments are hard. Let's make a
121generator, `+send` to make everything nicer. Since it's specific
122to `:ping`, let's put it in `/gen/ping/send.hoon`:
124```
125:- %say
126|= [^ [[to=@p message=?(~ [text=@t ~])] ~]]
127[%ping-message to ?~(message 'howdy' text.message)]
128```
130A couple of new things here. Firstly, `message=?(~ [text=@t ~])`
131should be read as "the message is either null or a pair of text
132and null". Generator argument lists are always null-terminated,
133which makes it convenient to accept lists in tail position (which
134are particularly annoying without generators). `?(a b)` is the
135irregular form of `$?(a b)`, which is a union type. Thus, `?(a
136b)` means the type of anything that's in either `a` or `b`.
137Thus, in our case, `?(~ [text=@t ~])` is either null or a pair of
138text and null.
140Secondly, `?~(a b c)` is a rune which means "if the `a` is null,
141do `b`, else `c`". It is roughly equivalent to `?:(=(~ a) b c)`.
143> `?~(a b c)` is actually equivalent to `?:=(?=(~ a) b c)`,
144> which, although identical at run time, is subtly different at
145> compile time. Specifically, using `?=` rather than `=` means
146> that we're checking whether `a` is in the *type* of `~`, and so
147> the compiler knows that in the `b` case `a` is null, and in the
148> `c` case `a` is not null. Since `=` is purely a runtime value
149> check with no type implications, the compiler doesn't gain any
150> information.
152> At any rate, this is the reason why we can refer to
153> `text.message` in the `c` clause. The compiler knows that
154> `message` is not null, so it must have `text` within it. If
155> you used `?:(=(~ message) 'howdy' text.message)` the compiler
156> would complain that it doesn't know whether `message` has
157> `text` within it.
159This is run as follows:
161```
162~fintud-macrep:dojo> :ping|send ~sampel-sipnym
163>=
164~fintud-macrep:dojo> :ping|send ~sampel-sipnym 'how do you do'
165>=
166```
168Which causes `~sampel-sipnym` to print `[%receiving 'howdy']` and
169`[%receiving 'how do you do']`.
171**Exercises**:
173- Create a generator for `:sum` from [State](state) so that
174 you can run `:sum|add 5` to add numbers to it.
176- Create a generator for `:click` from [Web Apps](web-apps) so
177 that you can run `:click|poke` to poke it.