on master help sign in
Removed outdated async-monad tutorial. Later will replace with threads
commit56bea0b69154f87a0fba4f8017e0aac022375fc0 authorDavid Kerschner <dkerschner@hcoop.net> dateTue 12 May 2020, 13:34 (6 years ago) parentdda94e6
1 file changed−215
tutorials/hoon/async-monad.md−215
tutorials/hoon/async-monad.mddeleted−215
@@ -1,215 +0,0 @@
1−+++
2−title = "2.7.2 Gall: Async Monad"
3−weight = 37
4−template = "doc.html"
5−+++
6−
7−Gall apps can be difficult to understand and simple ones often don't need the full complexity available when writing standard Gall apps. To that end, the `tapp` library implements the "async monad" which is more accurately described as a transitional IO monad. If you're not familiar with monads, don't worry as that understanding will not be necessary to use the `tapp` library.
8−
9−The following code is from `app/example-tapp-fetch.hoon` Much of this file should already be pretty readable, particularly given the comments in it, so we'll focus here on specific parts of the file rather than a full walkthrough. This app is intended to get the top comment of the top ten stories on Hacker News.
10−
11−```hoon
12−:: Little app to demonstrate the structure of programs written with the
13−:: transaction monad.
14−::
15−:: Fetches the top comment of each of the top 10 stories from Hacker News
16−::
17−/+ tapp, stdio
18−::
19−:: Preamble
20−::
21−=>
22− |%
23− +$ state
24− $: top-comments=(list tape)
25− ==
26− +$ peek-data _!!
27− +$ in-poke-data [%noun =cord]
28− +$ out-poke-data ~
29− +$ in-peer-data ~
30− +$ out-peer-data
31− $% [%comments (list tape)]
32− ==
33− ++ tapp (^tapp state peek-data in-poke-data out-poke-data in-peer-data out-peer-data)
34− ++ stdio (^stdio out-poke-data out-peer-data)
35− --
36−=>
37− |%
38− :: Helper function to print a comment
39− ::
40− ++ comment-to-tang
41− |= =tape
42− ^- tang
43− %+ welp
44− %+ turn (rip 10 (crip tape))
45− |= line=cord
46− leaf+(trip line)
47− [leaf+""]~
48− ::
49− :: All the URLs we fetch from
50− ::
51− ++ urls
52− =/ base "https://hacker-news.firebaseio.com/v0/"
53− :* top-stories=(weld base "topstories.json")
54− item=|=(item=@ud `tape`:(welp base "item/" +>:(scow %ui item) ".json"))
55− ==
56− --
57−=, async=async:tapp
58−=, tapp-async=tapp-async:tapp
59−=, stdio
60−::
61−:: The app
62−::
63−%- create-tapp-poke-peer-take:tapp
64−^- tapp-core-poke-peer-take:tapp
65−|_ [=bowl:gall state]
66−::
67−:: Main function
68−::
69−++ handle-poke
70− |= =in-poke-data
71− =/ m tapp-async
72− ^- form:m
73− ::
74− :: If requested to print, just print what we have in our state
75− ::
76− ?: =(cord.in-poke-data 'print')
77− ~& 'drumroll please...'
78− ;< now=@da bind:m get-time
79− ;< ~ bind:m (wait (add now ~s3))
80− ~& 'Top comments:'
81− %- (slog (zing (turn top-comments comment-to-tang)))
82− (pure:m top-comments)
83− ?: =(cord.in-poke-data 'poll')
84− ;< ~ bind:m (wait-effect (add now.bowl ~s15))
85− (pure:m top-comments)
86− ::
87− :: Otherwise, fetch the top HN stories
88− ::
89− =. top-comments ~
90− ::
91− :: If this whole thing takes more than 15 seconds, cancel it
92− ::
93− %+ (set-timeout _top-comments) (add now.bowl ~s15)
94− ;< =top-stories=json bind:m (fetch-json top-stories:urls)
95− =/ top-stories=(list @ud)
96− ((ar ni):dejs:format top-stories-json)
97− ::
98− :: Loop through the first 5 stories
99− ::
100− =. top-stories (scag 5 top-stories)
101− |- ^- form:m
102− =* loop $
103− ::
104− :: If done, tell subscribers and print the results
105− ::
106− ?~ top-stories
107− ;< ~ bind:m (give-result /comments %comments top-comments)
108− (handle-poke %noun 'print')
109− ::
110− :: Else, fetch the story info
111− ::
112− ~& "fetching item #{+>:(scow %ui i.top-stories)}"
113− ;< =story-info=json bind:m (fetch-json (item:urls i.top-stories))
114− =/ story-comments=(unit (list @ud))
115− ((ot kids+(ar ni) ~):dejs-soft:format story-info-json)
116− ::
117− :: If no comments, say so
118− ::
119− ?: |(?=(~ story-comments) ?=(~ u.story-comments))
120− =. top-comments ["<no top comment>" top-comments]
121− loop(top-stories t.top-stories)
122− ::
123− :: Else, fetch comment info
124− ::
125− ;< =comment-info=json bind:m (fetch-json (item:urls i.u.story-comments))
126− =/ comment-text=(unit tape)
127− ((ot text+sa ~):dejs-soft:format comment-info-json)
128− ::
129− :: If no text (eg comment deleted), record that
130− ::
131− ?~ comment-text
132− =. top-comments ["<top comment has no text>" top-comments]
133− loop(top-stories t.top-stories)
134− ::
135− :: Else, add text to state
136− ::
137− =. top-comments [u.comment-text top-comments]
138− loop(top-stories t.top-stories)
139−::
140−++ handle-peer
141− |= =path
142− =/ m tapp-async
143− ^- form:m
144− ~& [%tapp-fetch-take-peer path]
145− (pure:m top-comments)
146−::
147−++ handle-take
148− |= =sign:tapp
149− =/ m tapp-async
150− ^- form:m
151− :: ignore %poke/peer acknowledgements
152− ::
153− ?. ?=(%wake -.sign)
154− (pure:m top-comments)
155− ;< =state bind:m (handle-poke %noun 'fetch')
156− =. top-comments state
157− (pure:m top-comments)
158−--
159−```
160−
161−As indicated by the comments, `handle-poke` is the main [arm](/docs/glossary/arm/) of our Gall app. This is equivalent to the `poke` arms in a standard Gall app, but will not differentiate by mark. Let's look at the first part of this [gate](/docs/glossary/gate/).
162−
163−```hoon
164−++ handle-poke
165− |= =in-poke-data
166− =/ m tapp-async
167− ^- form:m
168− ::
169− :: If requested to print, just print what we have in our state
170− ::
171− ?: =(cord.in-poke-data 'print')
172− ~& 'drumroll please...'
173− ;< now=@da bind:m get-time
174− ;< ~ bind:m (wait (add now ~s3))
175− ~& 'Top comments:'
176− %- (slog (zing (turn top-comments comment-to-tang)))
177− (pure:m top-comments)
178− ?: =(cord.in-poke-data 'poll')
179− ;< ~ bind:m (wait-effect (add now.bowl ~s15))
180− (pure:m top-comments)
181−```
182−
183−The first part of this gate inspects the `in-poke-data` structure, specifically the `cord` to determine what request is being made. Here the request is to just print the data that we already have.
184−
185−The most important part to see here is the `;<` rune. If you're familiar with monadic bind, this is what this rune is. If you're not, don't worry! While we wont be discussing the implementation details of this rune, it's quite simple to use.
186−
187−`;<` has four children: `A`, `B`, `C` and `D`.
188−
189−`A` is a mold that describes the type produced by `C`.
190−
191−`B` is the monadic bind function, `bind:tapp-async`.
192−
193−`C` is some code we want to run and `D` is code to run after the `C` has completed successfully. If `C` fails, then the entire `;<` call will fail. This will allow us to chain a series of calls which may take a long time to complete, such as http requests, together and proceed when they actually complete.
194−
195−`D` must produce the type defined by `B`, `tapp-async` or `(async ,state)`. In the final leg of the `;<` series in this app you'll see the calls to `pure:m` which is used to wrap `top-comments` in an `async`.
196−
197−In a number of cases `D` here is going to be another `;<` rune. This is how you can chain these together to perform a lot of asynchronous actions that need to all complete.
198−
199−```hoon
200− ::
201− :: Otherwise, fetch the top HN stories
202− ::
203− =. top-comments ~
204− ::
205− :: If this whole thing takes more than 15 seconds, cancel it
206− ::
207− %+ (set-timeout _top-comments) (add now.bowl ~s15)
208− ;< =top-stories=json bind:m (fetch-json top-stories:urls)
209− =/ top-stories=(list @ud)
210− ((ar ni):dejs:format top-stories-json)
211−```
212−
213−Continuing on, here is a use of `fetch-json` with the `;<` that is an example worth reading. `fetch-json` takes a `tape` and produces a `form:(async ,json)` which can be processed to obtain the data we want.
214−
215−Armed with an understanding of `;<`, the rest of `handle-poke` should be quite readable though you may need to consult the `dejs-soft:format` [core](/docs/glossary/core/) to get a firm grasp on how JSON parsing works. Hopefully, you now have an understanding of the basic use of the `tapp` library. This style of Gall app can result in code that, while potentially less flexible, is easier to read and reason about than a traditional Gall app.