d2771c1108f627ef74234dda061663f9c94c30f9
[8sync.git] / doc / 8sync-new-manual.org
1 # Permission is granted to copy, distribute and/or modify this document
2 # under the terms of the GNU Free Documentation License, Version 1.3
3 # or any later version published by the Free Software Foundation;
4 # with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
5 # A copy of the license is included in the section entitled ``GNU
6 # Free Documentation License''.
7
8 # A copy of the license is also available from the Free Software
9 # Foundation Web site at http://www.gnu.org/licenses/fdl.html
10
11 # Altenately, this document is also available under the Lesser General
12 # Public License, version 3 or later, as published by the Free Software
13 # Foundation.
14
15 # A copy of the license is also available from the Free Software
16 # Foundation Web site at http://www.gnu.org/licenses/lgpl.html
17
18 * Preface
19
20 Welcome to 8sync's documentation!
21 8sync is an asynchronous programming environment for GNU Guile.
22 (Get it? 8sync? Async??? Quiet your groans, it's a great name!)
23
24 8sync has some nice properties:
25
26  - 8sync uses the actor model as its fundamental concurrency
27    synchronization mechanism.
28    Since the actor model is a "shared nothing" asynchronous
29    environment, you don't need to worry about deadlocks or other
30    tricky problems common to other asynchronous models.
31    Actors are modular units of code and state which communicate
32    by sending messages to each other.
33  - If you've done enough asynchronous programming, you're probably
34    familiar with the dreaded term "callback hell".
35    Getting around callback hell usually involves a tradeoff of other,
36    still rather difficult to wrap your brain around programming
37    patterns.
38    8sync uses some clever tricks involving "delimited continuations"
39    under the hood to make the code you write look familiar and
40    straightforward.
41    When you need to send a request to another actor and get some
42    information back from it without blocking, there's no need
43    to write a separate procedure... 8sync's scheduler will suspend
44    your procedure and wake it back up when a response is ready.
45  - Even nonblocking I/O code is straightforward to write.
46    Thanks to the "suspendable ports" code introduced in Guile 2.2,
47    writing asynchronous, nonblocking networked code looks mostly
48    like writing the same synchronous code.
49    8sync's scheduler handles suspending and resuming networked
50    code that would otherwise block.
51  - 8sync aims to be "batteries included".
52    Useful subsystems for IRC bots, HTTP servers, and so on are
53    included out of the box.
54  - 8sync prioritizes live hacking.
55    If using an editor like Emacs with a nice mode like Geiser,
56    an 8sync-using developer can change and fine-tune the behavior
57    of code /while it runs/.
58    This makes both debugging and development much more natural,
59    allowing the right designs to evolve under your fingertips.
60    A productive hacker is a happy hacker, after all!
61
62 In the future, 8sync will also provide the ability to spawn and
63 communicate with actors on different threads, processes, and machines,
64 with most code running the same as if actors were running in the same
65 execution environment.
66
67 But as a caution, 8sync is still very young.
68 The API is stabilizing, but not yet stable, and it is not yet well
69 "battle-tested".
70 Hacker beware!
71 But, consider this as much an opportunity as a warning.
72 8sync is in a state where there is much room for feedback and
73 contributions.
74 Your help wanted!
75
76 And now, into the wild, beautiful frontier.
77 Onward!
78
79 * Tutorial
80
81 ** Intro to the tutorial
82
83 IRC!  Internet Relay Chat!
84 The classic chat protocol of the Internet.
85 And it turns out, one of the best places to learn about networked
86 programming.
87
88 In the 1990s I remember stumbling into some funky IRC chat rooms and
89 being astounded that people there had what they called "bots" hanging
90 around.
91 From then until now, I've always enjoyed encountering bots whose range
92 of functionality has spanned from saying absurd things, to taking
93 messages when their "owners" were offline, to reporting the weather,
94 to logging meetings for participants.
95 And it turns out, IRC bots are a great way to cut your teeth on
96 networked programming; since IRC is a fairly simple line-delineated
97 protocol, it's a great way to learn to interact with sockets.
98 (My first IRC bot helped my team pick a place to go to lunch, previously
99 a source of significant dispute!)
100 At the time of writing, venture capital awash startups are trying to
101 turn chatbots into "big business"... a strange (and perhaps absurd)
102 thing given chat bots being a fairly mundane novelty amongst hackers
103 and teenagers everywhere in the 1990s.
104
105 We ourselves are going to explore chat bots as a basis for getting our
106 feet wet in 8sync.
107 We'll start from a minimalist example using an irc bot with most of
108 the work done for us, then move on to constructing our own actors as
109 "game pieces" which interface with our bot, then experiment with just
110 how easy it is to add new networked layers by tacking on a high score
111 to our game, and as a finale we'll dive into writing our own little
112 irc bot framework "from scratch" on top of the 8sync actor model.
113
114 Alright, let's get started.
115 This should be a lot of fun!
116
117 ** A silly little IRC bot
118
119 First of all, we're going to need to import some modules.  Put this at
120 the top of your file:
121
122 #+BEGIN_SRC scheme
123   (use-modules (8sync)               ; 8sync's agenda and actors
124                (8sync systems irc)   ; the irc bot subsystem
125                (oop goops)           ; 8sync's actors use GOOPS
126                (ice-9 format)        ; basic string formatting
127                (ice-9 match))        ; pattern matching
128 #+END_SRC
129
130 Now we need to add our bot.  Initially, it won't do much.
131
132 #+BEGIN_SRC scheme
133   (define-class <my-irc-bot> (<irc-bot>))
134
135   (define-method (handle-line (irc-bot <my-irc-bot>) speaker channel
136                               line emote?)
137     (if emote?
138         (format #t "~a emoted ~s in channel ~a\n"
139                 speaker line channel)
140         (format #t "~a said ~s in channel ~a\n"
141                 speaker line channel)))
142 #+END_SRC
143
144 We've just defined our own IRC bot!
145 This is an 8sync actor.
146 (8sync uses GOOPS to define actors.)
147 We extended the handle-line generic method, so this is the code that
148 will be called whenever the IRC bot "hears" anything.
149 For now the code is pretty basic: it just outputs whatever it "hears"
150 from a user in a channel to the current output port.
151 Pretty boring!
152 But it should help us make sure we have things working when we kick
153 things off.
154
155 Speaking of, even though we've defined our actor, it's not running
156 yet.  Time to fix that!
157
158 #+BEGIN_SRC scheme
159 (define* (run-bot #:key (username "examplebot")
160                   (server "irc.freenode.net")
161                   (channels '("##botchat")))
162   (define hive (make-hive))
163   (define irc-bot
164     (bootstrap-actor* hive <my-irc-bot> "irc-bot"
165                       #:username username
166                       #:server server
167                       #:channels channels))
168   (run-hive hive (list (bootstrap-message hive irc-bot 'init))))
169 #+END_SRC
170
171 Actors are connected to something called a "hive", which is a
172 special kind of actor that runs all the other actors.
173 Actors can spawn other actors, but before we start the hive we use
174 this special "bootstrap-actor*" method.
175 It takes the hive as its first argument, the actor class as the second
176 argument, a decorative "cookie" as the third argument (this is
177 optional, but it helps with debugging... you can skip it by setting it
178 to #f if you prefer), and the rest are initialization arguments to the
179 actor.  bootstrap-actor* passes back not the actor itself (we don't
180 get access to that usually) but the *id* of the actor.
181 (More on this later.)
182 Finally we run the hive with run-hive and pass it a list of
183 "bootstrapped" messages.
184 Normally actors send messages to each other (and sometimes themselves),
185 but we need to send a message or messages to start things or else
186 nothing is going to happen.
187
188 We can run it like:
189
190 #+BEGIN_SRC scheme
191 (run-bot #:username "some-bot-username") ; be creative!
192 #+END_SRC
193
194 Assuming all the tubes on the internet are properly connected, you
195 should be able to join the "##botchat" channel on irc.freenode.net and
196 see your bot join as well.
197 Now, as you probably guessed, you can't really /do/ much yet.
198 If you talk to the bot, it'll send messages to the terminal informing
199 you as such, but it's hardly a chat bot if it's not chatting yet.
200
201 So let's do the most boring (and annoying) thing possible.
202 Let's get it to echo whatever we say back to us.
203 Change handle-line to this:
204
205 #+BEGIN_SRC scheme
206   (define-method (handle-line (irc-bot <my-irc-bot>) speaker channel
207                               line emote?)
208     (<- (actor-id irc-bot) 'send-line channel
209         (format #f "Bawwwwk! ~a says: ~a" speaker line)))
210 #+END_SRC
211
212 This will do exactly what it looks like: repeat back whatever anyone
213 says like an obnoxious parrot.
214 Give it a try, but don't keep it running for too long... this
215 bot is so annoying it's likely to get banned from whatever channel
216 you put it in.
217
218 This method handler does have the advantage of being simple though.
219 It introduces a new concept simply... sending a message!
220 Whenever you see "<-", you can think of that as saying "send this
221 message".
222 The arguments to "<-" are as follows: the actor sending the message,
223 the id of the actor the message is being sent to, the "action" we
224 want to invoke (a symbol), and the rest are arguments to the
225 "action handler" which is in this case send-line (with itself takes
226 two arguments: the channel our bot should send a message to, and
227 the line we want it to spit out to the channel).
228
229 (Footnote: 8sync's name for sending a message, "<-", comes from older,
230 early lisp object oriented systems which were, as it turned out,
231 inspired by the actor model!
232 Eventually message passing was dropped in favor of something called
233 "generic functions" or "generic methods"
234 (you may observe we made use of such a thing in extending
235 handle-line).
236 Many lispers believe that there is no need for message passing
237 with generic methods and some advanced functional techniques,
238 but in a concurrent environment message passing becomes useful
239 again, especially when the communicating objects / actors are not
240 in the same address space.)
241
242 Normally in the actor model, we don't have direct references to
243 an actor, only an identifier.
244 This is for two reasons: to quasi-enforce the "shared nothing"
245 environment (actors absolutely control their own resources, and
246 "all you can do is send a message" to request that they modify
247 them) and because... well, you don't even know where that actor is!
248 Actors can be anything, and anywhere.
249 It's possible in 8sync to have an actor on a remote hive, which means
250 the actor could be on a remote process or even remote machine, and
251 in most cases message passing will look exactly the same.
252 (There are some exceptions; it's possible for two actors on the same
253 hive to "hand off" some special types of data that can't be serialized
254 across processes or the network, eg a socket or a closure, perhaps even
255 one with mutable state.
256 This must be done with care, and the actors should be careful both
257 to ensure that they are both local and that the actor handing things
258 off no longer accesses that value to preserve the actor model.
259 But this is an advanced topic, and we are getting ahead of ourselves.)
260 We have to supply the id of the receiving actor, and usually we'd have
261 only the identifier.
262 But since in this case, since the actor we're sending this to is
263 ourselves, we have to pass in our identifier, since the Hive won't
264 deliver to anything other than an address.
265
266 Astute readers may observe, since this is a case where we are just
267 referencing our own object, couldn't we just call "sending a line"
268 as a method of our own object without all the message passing?
269 Indeed, we do have such a method, so we /could/ rewrite handle-line
270 like so:
271
272 #+BEGIN_SRC scheme
273   (define-method (handle-line (irc-bot <my-irc-bot>) speaker channel
274                               line emote?)
275     (irc-bot-send-line irc-bot channel
276                        (format #f "Bawwwwk! ~a says: ~a" speaker line)))
277 #+END_SRC
278
279 ... but we want to get you comfortable and familiar with message
280 passing, and we'll be making use of this same message passing shortly
281 so that /other/ actors may participate in communicating with IRC
282 through our IRC bot.
283
284 Anyway, our current message handler is simply too annoying.
285 What would be much more interesting is if we could recognize
286 when an actor could repeat messages /only/ when someone is speaking
287 to it directly.
288 Luckily this is an easy adjustment to make.
289
290 #+BEGIN_SRC scheme
291   (define-method (handle-line (irc-bot <my-irc-bot>) speaker channel
292                               line emote?)
293     (define my-name (irc-bot-username irc-bot))
294     (define (looks-like-me? str)
295       (or (equal? str my-name)
296           (equal? str (string-concatenate (list my-name ":")))))
297     (when (looks-like-me?)
298       (<- (actor-id irc-bot) 'send-line channel
299           (format #f "Bawwwwk! ~a says: ~a" speaker line))))
300 #+END_SRC
301
302 This is relatively straightforward, but it isn't very interesting.
303 What we would really like to do is have our bot respond to individual
304 "commands" like this:
305
306 #+BEGIN_SRC text
307   <foo-user> examplebot: hi!
308   <examplebot> Oh hi foo-user!
309   <foo-user> examplebot: botsnack
310   <examplebot> Yippie! *does a dance!*
311   <foo-user> examplebot: echo I'm a very silly bot
312   <examplebot> I'm a very silly bot
313 #+END_SRC
314
315 Whee, that looks like fun!
316 To implement it, we're going to pull out Guile's pattern matcher.
317
318 #+BEGIN_SRC scheme
319   (define-method (handle-line (irc-bot <my-irc-bot>) speaker channel
320                               line emote?)
321     (define my-name (irc-bot-username irc-bot))
322     (define (looks-like-me? str)
323       (or (equal? str my-name)
324           (equal? str (string-concatenate (list my-name ":")))))
325     (match (string-split line #\space)
326       (((? looks-like-me? _) action action-args ...)
327        (match action
328          ;; The classic botsnack!
329          ("botsnack"
330           (<- (actor-id irc-bot) 'send-line channel
331               "Yippie! *does a dance!*"))
332          ;; Return greeting
333          ((or "hello" "hello!" "hello." "greetings" "greetings." "greetings!"
334               "hei" "hei." "hei!" "hi" "hi!")
335           (<- (actor-id irc-bot) 'send-line channel
336               (format #f "Oh hi ~a!" speaker)))
337          ("echo"
338           (<- (actor-id irc-bot) 'send-line channel
339               (string-join action-args " ")))
340
341          ;; --->  Add yours here <---
342
343          ;; Default
344          (_
345           (<- (actor-id irc-bot) 'send-line channel
346               "*stupid puppy look*"))))))
347 #+END_SRC
348
349 Parsing the pattern matcher syntax is left as an exercise for the
350 reader.
351
352 If you're getting the sense that we could make this a bit less wordy,
353 you're right:
354
355 #+BEGIN_SRC scheme
356   (define-method (handle-line (irc-bot <my-irc-bot>) speaker channel
357                               line emote?)
358     (define my-name (irc-bot-username irc-bot))
359     (define (looks-like-me? str)
360       (or (equal? str my-name)
361           (equal? str (string-concatenate (list my-name ":")))))
362     (define (respond respond-line)
363       (<- (actor-id irc-bot) 'send-line channel
364           respond-line))
365     (match (string-split line #\space)
366       (((? looks-like-me? _) action action-args ...)
367        (match action
368          ;; The classic botsnack!
369          ("botsnack"
370           (respond "Yippie! *does a dance!*"))
371          ;; Return greeting
372          ((or "hello" "hello!" "hello." "greetings" "greetings." "greetings!"
373               "hei" "hei." "hei!" "hi" "hi." "hi!")
374           (respond (format #f "Oh hi ~a!" speaker)))
375          ("echo"
376           (respond (string-join action-args " ")))
377
378          ;; --->  Add yours here <---
379
380          ;; Default
381          (_
382           (respond "*stupid puppy look*"))))))
383 #+END_SRC
384
385 Okay, that looks pretty good!
386 Now we have enough information to build an IRC bot that can do a lot
387 of things.
388 Take some time to experiment with extending the bot a bit before
389 moving on to the next section!
390 What cool commands can you add?
391
392 ** An intermission: about live hacking
393
394 This section is optional, but highly recommended.
395 It requires that you're a user of GNU Emacs.
396 If you aren't, don't worry... you can forge ahead and come back in case
397 you ever do become an Emacs user.
398 (If you're more familiar with Vi/Vim style editing, I hear good things
399 about Spacemacs...)
400
401 So you may have noticed while updating the last section that the
402 start/stop cycle of hacking isn't really ideal.
403 You might either edit a file in your editor, then run it, or
404 type the whole program into the REPL, but then you'll have to spend
405 extra time copying it to a file.
406 Wouldn't it be nice if it were possible to both write code in a
407 file and try it as you go?
408 And wouldn't it be even better if you could live edit a program
409 while it's running?
410
411 Luckily, there's a great Emacs mode called Geiser which makes
412 editing and hacking and experimenting all happen in harmony.
413 And even better, 8sync is optimized for this experience.
414 8sync provides easy drop-in "cooperative REPL" support, and
415 most code can be simply redefined on the fly in 8sync through Geiser
416 and actors will immediately update their behavior, so you can test
417 and tweak things as you go.
418
419 Okay, enough talking.  Let's add it!
420 Redefine run-bot like so:
421
422 #+BEGIN_SRC scheme
423   (define* (run-bot #:key (username "examplebot")
424                     (server "irc.freenode.net")
425                     (channels '("##botchat"))
426                     (repl-path "/tmp/8sync-repl"))
427     (define hive (make-hive))
428     (define irc-bot
429       (bootstrap-actor* hive <my-irc-bot> "irc-bot"
430                         #:username username
431                         #:server server
432                         #:channels channels))
433     (define repl-manager
434       (bootstrap-actor* hive <repl-manager> "repl"
435                           #:path repl-path))
436
437     (run-hive hive (list (bootstrap-message hive irc-bot 'init)
438                          (bootstrap-message hive repl-manager 'init))))
439 #+END_SRC
440
441 If we put a call to run-bot at the bottom of our file we can call it,
442 and the repl-manager will start something we can connect to automatically.
443 Horray!
444 Now when we run this it'll start up a REPL with a unix domain socket at
445 the repl-path.
446 We can connect to it in emacs like so:
447
448 : M-x geiser-connect-local <RET> guile <RET> /tmp/8sync-repl <RET>
449
450 Okay, so what does this get us?
451 Well, we can now live edit our program.
452 Let's change how our bot behaves a bit.
453 Let's change handle-line and tweak how the bot responds to a botsnack.
454 Change this part:
455
456 #+BEGIN_SRC scheme
457   ;; From this:
458   ("botsnack"
459    (respond "Yippie! *does a dance!*"))
460
461   ;; To this:
462   ("botsnack"
463    (respond "Yippie! *catches botsnack in midair!*"))
464 #+END_SRC
465
466 Okay, now let's evaluate the change of the definition.
467 You can hit "C-M-x" anywhere in the definition to re-evaluate.
468 (You can also position your cursor at the end of the definition and press
469 "C-x C-e", but I've come to like "C-M-x" better because I can evaluate as soon
470 as I'm done writing.)
471 Now, on IRC, ask your bot for a botsnack.
472 The bot should give the new message... with no need to stop and start the
473 program!
474
475 Let's fix a bug live.
476 Our current program works great if you talk to your bot in the same
477 IRC channel, but what if you try to talk to them over private message?
478
479 #+BEGIN_SRC text
480 IRC> /query examplebot
481 <foo-user> examplebot: hi!
482 #+END_SRC
483
484 Hm, we aren't seeing any response on IRC!
485 Huh?  What's going on?
486 It's time to do some debugging.
487 There are plenty of debugging tools in Guile, but sometimes the simplest
488 is the nicest, and the simplest debugging route around is good old
489 fashioned print debugging.
490
491 It turns out Guile has an under-advertised feature which makes print
492 debugging really easy called "pk", pronounced "peek".
493 What pk accepts a list of arguments, prints out the whole thing,
494 but returns the last argument.
495 This makes wrapping bits of our code pretty easy to see what's
496 going on.
497 So let's peek into our program with pk.
498 Edit the respond section to see what channel it's really sending
499 things to:
500
501 #+BEGIN_SRC scheme
502   (define-method (handle-line (irc-bot <my-irc-bot>) speaker channel
503                               line emote?)
504     ;; [... snip ...]
505     (define (respond respond-line)
506       (<- (actor-id irc-bot) 'send-line (pk 'channel channel)
507           respond-line))
508     ;; [... snip ...]
509     )
510 #+END_SRC
511
512 Re-evaluate.
513 Now let's ping our bot in both the channel and over PM.
514
515 #+BEGIN_SRC text
516 ;;; (channel "##botchat")
517
518 ;;; (channel "sinkbot")
519 #+END_SRC
520
521 Oh okay, this makes sense.
522 When we're talking in a normal multi-user channel, the channel we see
523 the message coming from is the same one we send to.
524 But over PM, the channel is a username, and in this case the username
525 we're sending our line of text to is ourselves.
526 That isn't what we want.
527 Let's edit our code so that if we see that the channel we're sending
528 to looks like our own username that we respond back to the sender.
529 (We can remove the pk now that we know what's going on.)
530
531 #+BEGIN_SRC scheme
532   (define-method (handle-line (irc-bot <my-irc-bot>) speaker channel
533                               line emote?)
534     ;; [... snip ...]
535     (define (respond respond-line)
536       (<- (actor-id irc-bot) 'send-line
537           (if (looks-like-me? channel)
538               speaker    ; PM session
539               channel)   ; normal IRC channel
540           respond-line))
541     ;; [... snip ...]
542     )
543 #+END_SRC
544
545 Re-evaluate and test.
546
547 #+BEGIN_SRC text
548 IRC> /query examplebot
549 <foo-user> examplebot: hi!
550 <examplebot> Oh hi foo-user!
551 #+END_SRC
552
553 Horray!
554
555 ** Writing our own actors
556
557 Let's write the most basic, boring actor possible.
558 How about an actor that start sleeping, and keeps sleeping?
559
560 #+BEGIN_SRC scheme
561   (use-modules (oop goops)
562                (8sync))
563
564   (define-class <sleeper> (<actor>)
565     (actions #:allocation #:each-subclass
566              #:init-value (build-actions
567                            (loop sleeper-loop))))
568
569   (define (sleeper-loop actor message)
570     (while (actor-alive? actor)
571       (display "Zzzzzzzz....\n")
572       ;; Sleep for one second
573       (8sleep 1)))
574
575   (let* ((hive (make-hive))
576          (sleeper (bootstrap-actor hive <sleeper>)))
577     (run-hive hive (list (bootstrap-message hive sleeper 'loop))))
578 #+END_SRC
579
580 We see some particular things in this example.
581 One thing is that our <sleeper> actor has an actions slot.
582 This is used to look up what the "action handler" for a message is.
583 We have to set the #:allocation to either #:each-subclass or #:class.
584 (#:class should be fine, except there is [[https://debbugs.gnu.org/cgi/bugreport.cgi?bug=25211][a bug in Guile]] which keeps
585 us from using it for now.)
586
587 In our sleeper-loop we also see a call to "8sleep".
588 "8sleep" is like Guile's "sleep" method, except it is non-blocking
589 and will always yield to the scheduler.
590
591 Our while loop also checks "actor-alive?" to see whether or not
592 it is still registered.
593 In general, if you keep a loop in your actor that regularly yields
594 to the scheduler, you should check this.
595 (An alternate way to handle it would be to not use a while loop at all
596 but simply send a message to ourselves with "<-" to call the
597 sleeper-loop handler again.
598 If the actor was dead, the message simply would not be delivered and
599 thus the loop would stop.)
600
601 This actor is pretty lazy though.
602 Time to get back to work!
603
604 #+BEGIN_SRC scheme
605   (use-modules (8sync)
606                (oop goops))
607
608   (define-class <manager> (<actor>)
609     (direct-report #:init-keyword #:direct-report
610                    #:getter manager-direct-report)
611     (actions #:allocation #:each-subclass
612              #:init-value (build-actions
613                            (assign-task manager-assign-task))))
614
615   (define (manager-assign-task manager message difficulty)
616     "Delegate a task to our direct report"
617     (display "manager> Work on this task for me!\n")
618     (<- (manager-direct-report manager)
619         'work-on-this difficulty))
620 #+END_SRC
621
622 Here we're constructing a very simple manager actor.
623 This manager keeps track of a direct report and tells them to start
624 working on a task... simple delegation.
625 Nothing here is really new, but note that our friend "<-" (which means
626 "send message") is back.
627 There's one difference this time... the first time we saw "<-" was in
628 the handle-line procedure of the irc-bot, and in that case we explicitly
629 pulled the actor-id after the actor we were sending the message to
630 (ourselves), which we aren't doing here.
631 But that was an unusual case, because the actor was ourself.
632 In this case, and in general, actors don't have direct references to
633 other actors; instead, all they have is access to identifiers which
634 reference other actors.
635
636 #+BEGIN_SRC scheme
637   (define-class <worker> (<actor>)
638     (task-left #:init-keyword #:task-left
639                #:accessor worker-task-left)
640     (actions #:allocation #:each-subclass
641              #:init-value (build-actions
642                            (work-on-this worker-work-on-this))))
643
644   (define (worker-work-on-this worker message difficulty)
645     "Work on one task until done."
646     (set! (worker-task-left worker) difficulty)
647     (display "worker> Whatever you say, boss!\n")
648     (while (and (actor-alive? worker)
649                 (> (worker-task-left worker) 0))
650       (display "worker> *huff puff*\n")
651       (set! (worker-task-left worker)
652             (- (worker-task-left worker) 1))
653       (8sleep (/ 1 3))))
654 #+END_SRC
655
656 The worker also contains familiar code, but we now see that we can
657 call 8sleep with non-integer real numbers.
658
659 Looks like there's nothing left to do but run it:
660
661 #+BEGIN_SRC scheme
662   (let* ((hive (make-hive))
663          (worker (bootstrap-actor hive <worker>))
664          (manager (bootstrap-actor hive <manager>
665                                    #:direct-report worker)))
666     (run-hive hive (list (bootstrap-message hive manager 'assign-task 5))))
667 #+END_SRC
668
669 #+BEGIN_SRC text
670 manager> Work on this task for me!
671 worker> Whatever you say, boss!
672 worker> *huff puff*
673 worker> *huff puff*
674 worker> *huff puff*
675 worker> *huff puff*
676 worker> *huff puff*
677 #+END_SRC
678
679 "<-" pays no attention to what happens with the messages it has sent
680 off.
681 This is useful in many cases... we can blast off many messages and
682 continue along without holding anything back.
683
684 But sometimes we want to make sure that something completes before
685 we do something else, or we want to send a message and get some sort
686 of information back.
687 Luckily 8sync comes with an answer to that with "<-wait", which will
688 suspend the caller until the callee gives some sort of response, but
689 which does not block the rest of the program from running.
690 Let's try applying that to our own code by turning our manager
691 into a micromanager.
692
693 #+END_SRC
694 #+BEGIN_SRC scheme
695   ;;; Update this method
696   (define (manager-assign-task manager message difficulty)
697     "Delegate a task to our direct report"
698     (display "manager> Work on this task for me!\n")
699     (<- (manager-direct-report manager)
700         'work-on-this difficulty)
701
702     ;; Wait a moment, then call the micromanagement loop
703     (8sleep (/ 1 2))
704     (manager-micromanage-loop manager))
705
706   ;;; And add the following
707   ;;;   (... Note: do not model actual employee management off this)
708   (define (manager-micromanage-loop manager)
709     "Pester direct report until they're done with their task."
710     (display "manager> Are you done yet???\n")
711     (let ((worker-is-done
712            (mbody-val (<-wait (manager-direct-report manager)
713                               'done-yet?))))
714       (if worker-is-done
715           (begin (display "manager> Oh!  I guess you can go home then.\n")
716                  (<- (manager-direct-report manager) 'go-home))
717           (begin (display "manager> Harumph!\n")
718                  (8sleep (/ 1 2))
719                  (when (actor-alive? manager)
720                    (manager-micromanage-loop manager))))))
721 #+END_SRC
722
723 We've appended a micromanagement loop here... but what's going on?
724 "<-wait", as it sounds, waits for a reply, and returns a reply
725 message.
726 In this case there's a value in the body of the message we want,
727 so we pull it out with mbody-val.
728 (It's possible for a remote actor to return multiple values, in which
729 case we'd want to use mbody-receive, but that's a bit more
730 complicated.)
731
732 Of course, we need to update our worker accordingly as well.
733
734 #+BEGIN_SRC scheme
735   ;;; Update the worker to add the following new actions:
736   (define-class <worker> (<actor>)
737     (task-left #:init-keyword #:task-left
738                #:accessor worker-task-left)
739     (actions #:allocation #:each-subclass
740              #:init-value (build-actions
741                            (work-on-this worker-work-on-this)
742                            ;; Add these:
743                            (done-yet? worker-done-yet?)
744                            (go-home worker-go-home))))
745
746   ;;; New procedures:
747   (define (worker-done-yet? worker message)
748     "Reply with whether or not we're done yet."
749     (let ((am-i-done? (= (worker-task-left worker) 0)))
750       (if am-i-done?
751           (display "worker> Yes, I finished up!\n")
752           (display "worker> No... I'm still working on it...\n"))
753       (<-reply message am-i-done?)))
754
755   (define (worker-go-home worker message)
756     "It's off of work for us!"
757     (display "worker> Whew!  Free at last.\n")
758     (self-destruct worker))
759 #+END_SRC
760
761 Running it is the same as before:
762
763 #+BEGIN_SRC scheme
764   (let* ((hive (make-hive))
765          (worker (bootstrap-actor hive <worker>))
766          (manager (bootstrap-actor hive <manager>
767                                    #:direct-report worker)))
768     (run-hive hive (list (bootstrap-message hive manager 'assign-task 5))))
769 #+END_SRC
770
771 But the output is a bit different:
772
773 #+BEGIN_SRC scheme
774 manager> Work on this task for me!
775 worker> Whatever you say, boss!
776 worker> *huff puff*
777 worker> *huff puff*
778 manager> Are you done yet???
779 worker> No... I'm still working on it...
780 manager> Harumph!
781 worker> *huff puff*
782 manager> Are you done yet???
783 worker> *huff puff*
784 worker> No... I'm still working on it...
785 manager> Harumph!
786 worker> *huff puff*
787 manager> Are you done yet???
788 worker> Yes, I finished up!
789 manager> Oh!  I guess you can go home then.
790 worker> Whew!  Free at last.
791 #+END_SRC
792
793 "<-reply" is what actually returns the information to the actor
794 waiting on the reply.
795 It takes as an argument the actor sending the message, the message
796 it is in reply to, and the rest of the arguments are the "body" of
797 the message.
798 (If an actor handles a message that is being "waited on" but does not
799 explicitly reply to it, an auto-reply with an empty body will be
800 triggered so that the waiting actor is not left waiting around.)
801
802 The last thing to note is the call to "self-destruct".
803 This does what you might expect: it removes the actor from the hive.
804 No new messages will be sent to it.
805 Ka-poof!
806
807 ** Writing our own <irc-bot> from scratch
808
809 * API reference
810
811 * Systems reference
812 ** IRC
813 ** Web / HTTP
814 ** COMMENT Websockets
815
816 * Addendum
817 ** Recommended .emacs additions
818
819 In order for =mbody-receive= to indent properly, put this in your
820 .emacs:
821
822 #+BEGIN_SRC emacs-lisp
823 (put 'mbody-receive 'scheme-indent-function 2)
824 #+END_SRC
825
826 ** 8sync and Fibers
827
828 One other major library for asynchronous communication in Guile-land
829 is [[https://github.com/wingo/fibers/][Fibers]].
830 There's a lot of overlap:
831
832  - Both use Guile's suspendable-ports facility
833  - Both communicate between asynchronous processes using message passing;
834    you don't have to squint hard to see the relationship between Fibers'
835    channels and 8sync's actor inboxes.
836
837 However, there are clearly differences too.
838 There's a one to one relationship between 8sync actors and an actor inbox,
839 whereas each Fibers fiber may read from multiple channels, for example.
840
841 Luckily, it turns out there's a clear relationship, based on real,
842 actual theory!
843 8sync is based on the [[https://en.wikipedia.org/wiki/Actor_model][actor model]] whereas fibers follows
844 [[http://usingcsp.com/][Communicating Sequential Processes (CSP)]], which is a form of
845 [[https://en.wikipedia.org/wiki/Process_calculus][process calculi]]. 
846 And it turns out, the
847 [[https://en.wikipedia.org/wiki/Actor_model_and_process_calculi][relationship between the actor model and process calculi]] is well documented,
848 and even more precisely, the
849 [[https://en.wikipedia.org/wiki/Communicating_sequential_processes#Comparison_with_the_Actor_Model][relationship between CSP and the actor model]] is well understood too.
850
851 So, 8sync and Fibers do take somewhat different approaches, but both
852 have a solid theoretical backing... and their theories are well
853 understood in terms of each other.
854 Good news for theory nerds!
855
856 (Since the actors and CSP are [[https://en.wikipedia.org/wiki/Dual_%28mathematics%29][dual]], maybe eventually 8sync will be
857 implemented on top of Fibers... that remains to be seen!)
858