on 7fa97bd help sign in
urbit.org/docs/tutorials/gall/generators.umd
170 lines 5.9 kB history raw
1:- :~ navhome/'/docs/'
2 navuptwo/'true'
3 next/'true'
4 sort/'6'
5 title/'Using generators with apps'
6 ==
7;>
9# Generators
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
16sight.
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
24moves.
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`:
33```
34 ~fintud-macrep:dojo> +hood/merge %home our %they
35 [syd=%home her=~fintud-macrep sud=%they gem=%auto]
36 ~fintud-macrep:dojo> +hood/merge %home our %they, =gem %this
37 [syd=%home her=~fintud-macrep sud=%they gem=%auto]
38```
40This didn't run any merge, it just constructed the command that, if sent
41to `:hood`, would run it. Note first that, even though it's not printed,
42this value has mark `kiln-merge`. Also, the merge strategy `gem=%auto`
43was added automatically. Optional arguments are straightforward with
44generators.
46In general, generators are prepended by either `+` or `|`. The general
47form is `+generator`, but often generators are created specifically to
48work with a single app, they're usually placed in a subdirectory of gen
49and run with `|`. Thus, if `my-generator` generator is made for use with
50`:my-app`, then `my-generator` is put in
51`/gen/my-app/my-generator.hoon`, and can be run directly with
52`+my-app/my-generator <args>`. If you want to pipe the result into
53`:my-app`, then run `:my-app +my-app/my-generator <args>`. Because this
54pattern is so common, this can be abbreviated to
55`:my-app|my-generator <args>`. Because most built-in commands are
56generators for the `:hood` app, `:hood|generator <args>` can be
57shortened to `|generator <args>`.
59Let's write a generator for a modified version of `:examples-pong` from
60the chapter on [Network Messages](/docs/arvo/system/network). Recall
61that `:examples-pong` takes an urbit address, which is of mark `urbit`,
62and sends that urbit the message `'howdy'`. First--without a
63generator--let's make `:examples-ping` that does the same, except that
64it lets the user optionally specify the message as well.
66We'll need a new mark for our arguments. Let's call it
67`examples-ping-message`.
69> For app-specific marks, it's good style to prefix the name of the mark
70> with the name of the app. Since many apps have several such marks,
71> subdirectories in `/mar` are rendered as `-`, so that `ping-message`
72> is written in (/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:gall 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 generator,
136`+examples-send` to make everything nicer. Since it's specific to
137`: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 and
147null". Generator argument lists are always null-terminated, which makes
148it convenient to accept lists in tail position (which are particularly
149annoying without generators). `?(a b)` is the irregular form of
150`$?(a b)`, which is a union type. Thus, `?(a b)` means the type of
151anything that's in either `a` or `b`. Thus, in our case,
152`?(~ [text=@t ~])` is either null or a pair of text and null.
154This is run as follows:
156```
157 ~fintud-macrep:dojo> :examples-ping|send ~sampel-sipnym
158 >=
159 ~fintud-macrep:dojo> :examples-ping|send ~sampel-sipnym 'how do you do'
160 >=
161```
163Which causes `~sampel-sipnym` to print `[%receiving 'howdy']` and
164`[%receiving 'how do you do']`.
166*Exercises*:
168- Create a generator for `:examples-sum` from [State](../../state) so that you can run `:examples-sum|add 5` to add numbers to it.
170- Create a generator for `:examples-click` from [Web Apps](../../web-apps) so that you can run `:examples-click|poke` to poke it.