on master help sign in
command line interface app/shoe tutorial (#912)

* fresh initial commit * finish command-parser * finish example app walkthrough * trimming and rewording * finish app walkthrough * clarifications on demo app * using the app * move move trace section * mostly removing stuff * marks suggestions * rest of marks suggestions * Update tutorials/arvo/cli-tutorial.md Co-authored-by: Fang <git@fang.io> * Update tutorials/arvo/cli-tutorial.md Co-authored-by: Fang <git@fang.io> * Update tutorials/arvo/cli-tutorial.md Co-authored-by: Fang <git@fang.io> * Apply suggestions from code review Co-authored-by: Fang <git@fang.io> * minor edits * typo * cards are `card`s * Apply suggestions from code review final mark reviews Co-authored-by: Fang <git@fang.io> Co-authored-by: Fang <git@fang.io>

commit55c56a05b9211c2df292c21df56543ecf450d82a authorpoprox <51337059+syzygyzer@users.noreply.github.com> dateWed 29 Jul 2020, 16:45 (6 years ago) parentdb42a0d
1 file changed+420
tutorials/arvo/cli-tutorial.md+420
tutorials/arvo/cli-tutorial.mdnew file+420
@@ -0,0 +1,420 @@
1++++
2+title = "Command line interface apps"
3+weight = 13
4+template = "doc.html"
5++++
6+
7+# Command line interface apps
8+
9+## Introduction
10+
11+In this walkthrough we will go in-depth on how to build command line interface (CLI)
12+applications in Urbit using the `shoe` library.
13+
14+There are three CLI apps that currently ship with urbit - `%dojo`, `%chat-cli`,
15+and `%shoe`. You should be familiar with the former two, the latter is an example
16+app that shows off how the `shoe` library works that we will be looking at
17+closely. These are all Gall apps, and as such their source can be found in the `app/` folder of your `%home` desk.
18+
19+In [the `shoe` library](#the-shoe-library) we take a closer look at the `shoe` library and its
20+cores and how they are utilized in CLI apps. Then in [the `sole`
21+library](#the-sole-library) we look at what `shoe` effects ultimately break down
22+into. Finally in [`%shoe` app walkthrough](#shoe-app-walkthrough) we explore
23+the functionality of the `%shoe` app and then go through the code line-by-line.
24+
25+This tutorial can be
26+considered to be an application equivalent of the [Hoon school
27+lesson](@/docs/tutorials/hoon/generators.md#ask) on `sole` and `%ask`
28+generators, which only covers the bare minimum necessary to write generators
29+that take user input.
30+
31+
32+## The `shoe` library {#the-shoe-library}
33+
34+Here we describe how sessions are identified, the specialized `card`s that Gall agents
35+with the `shoe` library are able to utilize, and the different cores of `/lib/shoe.hoon` and their purpose.
36+
37+### Session identifiers
38+
39+An app using the `shoe` library will automatically track sessions by their `sole-id=@ta`.
40+These are opaque identifiers generated by the connecting client. An app using the `shoe`
41+library may be connected to by a local or remote ship in order to send commands,
42+and each of these connections is assigned a unique `@ta` that identifies the
43+ship and which session on that ship if there are multiple.
44+
45+### `%shoe` `card`s
46+
47+Gall agents with the `shoe` library are able to utilize `%shoe` `card`s. These
48+additions to the standard set of `cards` have the following shape:
49+```hoon
50+[%shoe sole-ids=(list @ta) effect=shoe-effect]`
51+```
52+`sole-ids` is the `list` of session ids that the following `effect` is
53+emitted to. An empty `sole-ids` sends the effect to all connected sessions.
54+`shoe-effect`s, for now, are always of the shape `[%sole effect=sole-effect]`, where
55+`sole-effect`s are basic console events such as displaying text, changing the
56+prompt, beeping, etc. These are described in the section on the [`sole` library](#the-sole-library).
57+
58+For example, a `%shoe` `card` that causes all connected sessions to beep would
59+be `[%shoe ~ %sole %bel ~]`.
60+
61+### `shoe` core
62+
63+An iron (contravariant) door that defines an interface for Gall agents utilizing
64+the `shoe` library. Use this core whenever you want to receive input from the user and run a command. The input will get
65+put through the parser (`+command-parser`) and results in a noun of
66+`command-type` that the underlying application specifies, which shoe then feeds back into the underlying app as an `+on-command` callback.
67+
68+In addition to the ten arms that all Gall core apps possess, `+shoe` defines and expects a few
69+more, tailored to common CLI logic. Thus you will need to wrap the `shoe:shoe` core using the `agent:shoe` function to obtain a standard
70+10-arm Gall agent core. See the [shoe example app
71+walkthrough](#shoe-example-app-walkthrough) for how to do this.
72+
73+The additional arms are described below. The Hoon code shows their expected type signature. As we'll see [later](#shoe-app-walkthrough), the `command-type` can differ per application. Note also that most of these take a session identifier as an argument. This lets applications provide different users (at potentially different "places" within the application) with different affordances.
74+
75+#### `+command-parser`
76+
77+```hoon
78+ ++ command-parser
79+ |~ sole-id=@ta
80+ |~(nail *(like [? command-type]))
81+```
82+
83+Input parser for a specific command-line session. Will be run on whatever the user tries to input into the command prompt, and won't let them type anything that doesn't parse. If the head of the result is true,
84+instantly run the command. If it's false, require the user to press return.
85+
86+#### `+tab-list`
87+
88+```hoon
89+ ++ tab-list
90+ |~ sole-id=@ta
91+ *(list (option:auto tank))
92+```
93+
94+Autocomplete options for the command-line session (to match `+command-parser`).
95+
96+#### `+on-command`
97+
98+```hoon
99+ ++ on-command
100+ |~ [sole-id=@ta command=command-type]
101+ *(quip card _this)
102+```
103+
104+Called when a valid command is run.
105+
106+#### `+can-connect`
107+
108+```hoon
109+ ++ can-connect
110+ |~ sole-id=@ta
111+ *?
112+```
113+
114+Called to determine whether a session may be opened or connected to. For example, you
115+may only want the local ship to be able to connect.
116+
117+#### `+on-connect`
118+
119+```hoon
120+ ++ on-connect
121+ |~ sole-id=@ta
122+ *(quip card _^|(..on-init))
123+```
124+
125+Called when a session is opened or connected to.
126+
127+#### `+on-disconnect`
128+
129+```hoon
130+ ++ on-disconnect
131+ |~ sole-id=@ta
132+ *(quip card _^|(..on-init))
133+```
134+
135+Called when a previously made session gets disconnected from.
136+
137+### `default` core
138+
139+This core contains the bare minimum implementation of the additional `shoe` arms
140+beyond the 10 standard Gall app ams. It is used
141+analogously to how the `default-agent` core is used for regular Gall apps.
142+
143+### `agent` core
144+
145+This is a function for wrapping a `shoe` core, which has too many
146+arms to be a valid Gall agent core. This turns it into a standard Gall agent core by
147+integrating the additional arms into the standard ones.
148+
149+
150+## The `sole` library {#the-sole-library}
151+
152+`shoe` apps may create specialized `card`s of the `[%shoe
153+(list @ta) shoe-effect]` shape, where `shoe-effect` currently just wrap `sole-effect`s, i.e. instructions for displaying text and producing other effects in the console.
154+
155+The list of possible `sole-effects` can be found in `/sur/sole.hoon`. A few
156+commonly used ones are as follows.
157+- `[%txt tape]` is used to display a line of text.
158+- `[%bel ~]` is used to emit a beep.
159+- `[%pro sole-prompt]` is used to set the prompt.
160+- `[%mor (list sole-effect)]` is used to emit multiple effects.
161+
162+For example, a `sole-effect` that beeps and displays `This is some text.` would
163+be structured as
164+```hoon
165+[%mor [%txt "This is some text."] [%bel ~] ~]
166+```
167+
168+## `%shoe` app walkthrough {#shoe-app-walkthrough}
169+
170+Here we explore the capabilities of the `%shoe` example app and then go through
171+the code, explaining what each line does.
172+
173+### Playing with `%shoe`
174+
175+First let's test the functionality of `%shoe` so we know what we're getting
176+into.
177+
178+Start two fake ships, one named `~zod` and the other can have any name - we will
179+go with `~nus`. Fake ships run locally are able to see each other, and our
180+intention is to connect their `%shoe` apps.
181+
182+On each fake ship start `%shoe` by entering `|start %shoe` into dojo. This will
183+automatically
184+change the prompt to `~zod:shoe>` and `~nus:shoe>`. Type `demo` and watch the following appear:
185+```
186+~zod ran the command
187+~zod:shoe>
188+```
189+`~zod ran the command` should be displayed in bold green text, signifying that
190+the command originated locally.
191+
192+Now we will connect the sessions. Switch `~zod` back to dojo with `Ctrl-X` and enter `|link
193+~nus %shoe`. If this succeeds you will see the following.
194+```
195+>=
196+; ~nus is your neighbor
197+[linked to [p=~nus q=%shoe]]
198+```
199+Now `~zod` will have two `%shoe` sessions running - one local one on `~zod` and
200+one remote one on `~nus`, which you can access by pressing `Ctrl-X` until you see
201+`~nus:shoe>` from `~zod`'s console. On the other hand, you should not see
202+`~zod:shoe>` on `~nus`'s side, since you have not connected `~nus` to `~zod`'s
203+`%shoe` app. When you enter `demo` from `~nus:shoe>` on
204+`~zod`'s console you will again see `~zod ran the command`, but this time it
205+should be in the ordinary font used by the console, signifying that the command
206+is originating from a remote session. Contrast this with entering `demo` from
207+`~nus:shoe>` in `~nus`'s console, which will display `~nus ran the command` in
208+bold green text.
209+
210+Now try to link to `~zod`'s `%shoe` session from `~nus` by switching to the dojo
211+on `~nus` and entering `|link ~zod %shoe`. You should see
212+```
213+>=
214+[unlinked from [p=~zod q=%shoe]]
215+```
216+and if you press `Ctrl-X` you will not get a `~zod:shoe>` prompt. This is
217+because the example app is set up to always allow `~zod` to connect (as well as
218+subject moons if the ship happens to be a planet) but not `~nus`, so this
219+message means that `~nus` failed to connect to `~zod`'s `%shoe` session.
220+
221+### `%shoe`'s code
222+
223+```hoon
224+:: shoe: example usage of /lib/shoe
225+::
226+:: the app supports one command: "demo".
227+:: running this command renders some text on all sole clients.
228+::
229+/+ shoe, verb, dbug, default-agent
230+```
231+
232+`/+` is the Ford rune which imports libraries from the `/lib` directory into
233+the subject.
234+ * `shoe` is the `shoe` library.
235+ * `verb` is a library used to print what a Gall agent is doing.
236+ * `dbug` is a library of debugging tools.
237+ * `default-agent` contains a Gall agent core with minimal implementations of
238+ required Gall arms.
239+
240+```hoon
241+|%
242++$ state-0 [%0 ~]
243++$ command ~
244+::
245++$ card card:shoe
246+--
247+```
248+The types used by the app.
249+
250+`state-0` stores the state of the app, which is null as there is no state to
251+keep track of. It is good practice to include a version number anyways,
252+in case the app is made stateful at a later time.
253+
254+`command` is typically a set of tagged union types that represent the possible
255+commands that can be entered by the user. Since this app only supports one
256+command, it is unnecessary for it to have any associated data, thus the command
257+is represented by `~`.
258+
259+In a non-trivial context, a `command` is commonly given by `[%name
260+data]`, where `%name` is the identifier for the type of command and `data` is
261+a type or list of types that contain data needed to execute the command. See
262+`app/chat-cli.hoon` for examples of commands, such as `[%say letter:store]` and
263+`[%delete path]`. This is not required though, and you could use something like
264+`[chat-room=@t =action]`.
265+
266+`card` is either an ordinary Gall agent `card` or a `%shoe` `card`, which takes
267+the shape `[%shoe sole-ids=(list @ta) effect=shoe-effect]`. A `%shoe` `card` is
268+sent to all `sole`s listed in `sole-ids`, imaking them run the `sole-effect`
269+specified by `effect` (i.e. printing some text). Here we can
270+reference `card:shoe` because of `/+ shoe` at the beginning of the app.
271+
272+```hoon
273+=| state-0
274+=* state -
275+::
276+```
277+Add the bunt value of `state-0` to the head of the subject, then give it the
278+macro `state`. The `-` here is a lark expression referring to the head of the
279+subject. This allows us to use `state` to refer to the state elsewhere in the
280+code no matter what version we're using, while also getting direct access to the
281+contents of `state` (if it had any).
282+
283+```hoon
284+%+ verb |
285+%- agent:dbug
286+^- agent:gall
287+%- (agent:shoe command)
288+^- (shoe:shoe command)
289+```
290+
291+The casts here are just reminders of what is being produced. So let's focus on
292+what the `%` runes are doing, from bottom to top. We call `(agent:shoe command)`
293+on what follows (i.e. the rest of the app),
294+producing a standard Gall agent core. Then we call wrap the Gall agent core with
295+`agent:dbug`, endowing it with additional arms useful for debugging, and then
296+wrap again with `verb`.
297+
298+
299+```hoon
300+|_ =bowl:gall
301++* this .
302+ def ~(. (default-agent this %|) bowl)
303+ des ~(. (default:shoe this command) bowl)
304+```
305+This is boilerplate Gall agent core code. We set `this` to be a macro for the
306+subject, which is the Gall agent core itself. We set `def` and `des` to be
307+macros for initialized `default-agent` and `default:shoe` doors respectively.
308+
309+Next we implement all of the arms required for a `shoe` agent. Starting with
310+the standard Gall arms:
311+
312+```hoon
313+++ on-init on-init:def
314+++ on-save !>(state)
315+++ on-load
316+ |= old=vase
317+ ^- (quip card _this)
318+ [~ this]
319+::
320+++ on-poke on-poke:def
321+++ on-watch on-watch:def
322+++ on-leave on-leave:def
323+++ on-peek on-peek:def
324+++ on-agent on-agent:def
325+++ on-arvo on-arvo:def
326+++ on-fail on-fail:def
327+```
328+These are minimalist Gall app arm implementations using the default behavior found in `def`.
329+
330+Here begins the implementation of the additional arms required by the
331+`(shoe:shoe command)` interface.
332+```hoon
333+++ command-parser
334+ |= sole-id=@ta
335+ ^+ |~(nail *(like [? command]))
336+ (cold [& ~] (jest 'demo'))
337+```
338+`+command-parser` is of central importance - it is what is used to parse user
339+input and transform it into `command`s for the app to execute. Writing a proper
340+command parser requires understanding of the Hoon parsing functions found in the
341+standard library. How to do so may be found in the parsing documentation (coming
342+soon). For now, it is sufficient to know that this arm matches the text "demo" and
343+produces a `[? command]`-shaped noun in response. Note how the `&` signifies that the command will be run as soon as it has been entered, without waiting for the user to press return.
344+
345+
346+```hoon
347+++ tab-list
348+ |= sole-id=@ta
349+ ^- (list [@t tank])
350+ :~ ['demo' leaf+"run example command"]
351+ ==
352+```
353+`+tab-list` is pretty much plug-n-play. For each command you want to be tab
354+completed, add an entry to the `list` begun by `:~` of the form `[%command
355+leaf+"description"]`. Now whenever the user types a partial command and presses
356+tab, the console will display the list of commmands that match the partial
357+command as well as the descriptions given here.
358+
359+Thus here we have that starting to type `demo` and pressing tab will result in
360+the following output in the console:
361+```
362+demo run example command
363+~zod:shoe> demo
364+```
365+with the remainder of `demo` now added to the input line.
366+
367+Next we have `+on-command`, which is called whenever `+command-parser`
368+recognizes that `demo` has been entered by a user.
369+```hoon
370+++ on-command
371+ |= [sole-id=@ta =command]
372+ ^- (quip card _this)
373+```
374+This is a gate that takes in the `sole-id` corresponding to the session and the
375+`command` noun parsed by `+command-parser` and returns a `list` of `card`s and
376+`_this`, which is our shoe agent core including its state.
377+```hoon
378+ =- [[%shoe ~ %sole -]~ this]
379+```
380+This creates a cell of a `%shoe` card that triggers a `sole-effect` given by the head of
381+the subject `-`, then the Gall agent core `this` - i.e. the return result of
382+this gate. The use of the `=-` rune means that what follows this
383+expression is actually run first, which puts the desired `sole-effect` into the
384+head of the subject.
385+```hoon
386+ =/ =tape "{(scow %p src.bowl)} ran the command"
387+```
388+We define the `tape` that we want to be printed.
389+```hoon
390+ ?. =(src our):bowl
391+ [%txt tape]
392+ [%klr [[`%br ~ `%g] [(crip tape)]~]~]
393+```
394+We cannot just produce the `tape` we want printed, - it needs to fit the
395+`sole-effect` type. This tells us that if the
396+origin of the command is not our ship to just print it normally with the `%txt`
397+`sole-effect`. Otherwise we use `%klr`, which prints it stylistically (here it
398+makes the text green and bold).
399+
400+The following allows either `~zod`, or the host ship and its moons, to connect to
401+this app's command line interface using `|link`.
402+```hoon
403+++ can-connect
404+ |= sole-id=@ta
405+ ^- ?
406+ ?| =(~zod src.bowl)
407+ (team:title [our src]:bowl)
408+ ==
409+```
410+
411+We use the minimal implementations for the final two `shoe` arms, since we don't
412+want to do anything special when users connect or disconnect.
413+```hoon
414+++ on-connect on-connect:des
415+++ on-disconnect on-disconnect:des
416+--
417+```
418+
419+This concludes our review of the code of the `%shoe` app. To continue learning
420+how to build your own CLI app, we recommend checking out `/app/chat-cli.hoon`.