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