dd3036e15b7121857c8e0700cc7b680fcd7d057a
[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 # Alternately, 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 ** A silly little IRC bot
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.[fn:irc-hacking]
87 We ourselves are going to explore chat bots as a basis for getting our
88 feet wet in 8sync.
89
90 First of all, we're going to need to import some modules.  Put this at
91 the top of your file:
92
93 #+BEGIN_SRC scheme
94   (use-modules (8sync)               ; 8sync's agenda and actors
95                (8sync systems irc)   ; the irc bot subsystem
96                (oop goops)           ; 8sync's actors use GOOPS
97                (ice-9 format)        ; basic string formatting
98                (ice-9 match))        ; pattern matching
99 #+END_SRC
100
101 Now we need to add our bot.  Initially, it won't do much.
102
103 #+BEGIN_SRC scheme
104   (define-class <my-irc-bot> (<irc-bot>))
105
106   (define-method (handle-line (irc-bot <my-irc-bot>) message
107                               speaker channel line emote?)
108     (if emote?
109         (format #t "~a emoted ~s in channel ~a\n"
110                 speaker line channel)
111         (format #t "~a said ~s in channel ~a\n"
112                 speaker line channel)))
113 #+END_SRC
114
115 We've just defined our own IRC bot!
116 This is an 8sync actor.
117 (8sync uses GOOPS to define actors.)
118 We extended the handle-line generic method, so this is the code that
119 will be called whenever the IRC bot "hears" anything.
120 This method is itself an action handler, hence the second argument
121 for =message=, which we can ignore for now.
122 Pleasantly, the message's argument body is passed in as the rest of
123 the arguments.
124
125 For now the code is pretty basic: it just outputs whatever it "hears"
126 from a user in a channel to the current output port.
127 Pretty boring!
128 But it should help us make sure we have things working when we kick
129 things off.
130
131 Speaking of, even though we've defined our actor, it's not running
132 yet.  Time to fix that!
133
134 #+BEGIN_SRC scheme
135 (define* (run-bot #:key (username "examplebot")
136                   (server "irc.freenode.net")
137                   (channels '("##botchat")))
138   (define hive (make-hive))
139   (define irc-bot
140     (bootstrap-actor hive <my-irc-bot>
141                      #:username username
142                      #:server server
143                      #:channels channels))
144   (run-hive hive '()))
145 #+END_SRC
146
147 Actors are connected to something called a "hive", which is a
148 special kind of actor that runs and manages all the other actors.
149 Actors can spawn other actors, but before we start the hive we use
150 this special =bootstrap-actor= method.
151 It takes the hive as its first argument, the actor class as the second
152 argument, and the rest are initialization arguments to the
153 actor.
154 =bootstrap-actor= passes back not the actor itself (we don't
155 get access to that usually) but the *id* of the actor.
156 (More on this later.)
157 Finally we run the hive with run-hive and pass it a list of
158 "bootstrapped" messages.
159 Normally actors send messages to each other (and sometimes themselves),
160 but we need to send a message or messages to start things or else
161 nothing is going to happen.
162
163 We can run it like:
164
165 #+BEGIN_SRC scheme
166 (run-bot #:username "some-bot-name") ; be creative!
167 #+END_SRC
168
169 Assuming all the tubes on the internet are properly connected, you
170 should be able to join the "##botchat" channel on irc.freenode.net and
171 see your bot join as well.
172 Now, as you probably guessed, you can't really /do/ much yet.
173 If you talk to the bot, it'll send messages to the terminal informing
174 you as such, but it's hardly a chat bot if it's not chatting yet.
175
176 So let's do the most boring (and annoying) thing possible.
177 Let's get it to echo whatever we say back to us.
178 Change handle-line to this:
179
180 #+BEGIN_SRC scheme
181   (define-method (handle-line (irc-bot <my-irc-bot>) message
182                               speaker channel line emote?)
183     (<- (actor-id irc-bot) 'send-line channel
184         (format #f "Bawwwwk! ~a says: ~a" speaker line)))
185 #+END_SRC
186
187 This will do exactly what it looks like: repeat back whatever anyone
188 says like an obnoxious parrot.
189 Give it a try, but don't keep it running for too long... this
190 bot is so annoying it's likely to get banned from whatever channel
191 you put it in.
192
193 This method handler does have the advantage of being simple though.
194 It introduces a new concept simply... sending a message!
195 Whenever you see "<-", you can think of that as saying "send this
196 message".
197 The arguments to "<-" are as follows: the actor sending the message,
198 the id of the actor the message is being sent to, the "action" we
199 want to invoke (a symbol), and the rest are arguments to the
200 "action handler" which is in this case send-line (with itself takes
201 two arguments: the channel our bot should send a message to, and
202 the line we want it to spit out to the channel).[fn:send-message-provenance]
203
204 Normally in the actor model, we don't have direct references to
205 an actor, only an identifier.
206 This is for two reasons: to quasi-enforce the "shared nothing"
207 environment (actors absolutely control their own resources, and
208 "all you can do is send a message" to request that they modify
209 them) and because... well, you don't even know where that actor is!
210 Actors can be anything, and anywhere.
211 It's possible in 8sync to have an actor on a remote hive, which means
212 the actor could be on a remote process or even remote machine, and
213 in most cases message passing will look exactly the same.
214 (There are some exceptions; it's possible for two actors on the same
215 hive to "hand off" some special types of data that can't be serialized
216 across processes or the network, eg a socket or a closure, perhaps even
217 one with mutable state.
218 This must be done with care, and the actors should be careful both
219 to ensure that they are both local and that the actor handing things
220 off no longer accesses that value to preserve the actor model.
221 But this is an advanced topic, and we are getting ahead of ourselves.)
222 We have to supply the id of the receiving actor, and usually we'd have
223 only the identifier.
224 But since in this case, since the actor we're sending this to is
225 ourselves, we have to pass in our identifier, since the Hive won't
226 deliver to anything other than an address.
227
228 Astute readers may observe, since this is a case where we are just
229 referencing our own object, couldn't we just call "sending a line"
230 as a method of our own object without all the message passing?
231 Indeed, we do have such a method, so we /could/ rewrite handle-line
232 like so:
233
234 #+BEGIN_SRC scheme
235   (define-method (handle-line (irc-bot <my-irc-bot>) message
236                               speaker channel line emote?)
237     (irc-bot-send-line irc-bot channel
238                        (format #f "Bawwwwk! ~a says: ~a" speaker line)))
239 #+END_SRC
240
241 ... but we want to get you comfortable and familiar with message
242 passing, and we'll be making use of this same message passing shortly
243 so that /other/ actors may participate in communicating with IRC
244 through our IRC bot.
245
246 Anyway, our current message handler is simply too annoying.
247 What we would really like to do is have our bot respond to individual
248 "commands" like this:
249
250 #+BEGIN_SRC text
251   <foo-user> examplebot: hi!
252   <examplebot> Oh hi foo-user!
253   <foo-user> examplebot: botsnack
254   <examplebot> Yippie! *does a dance!*
255   <foo-user> examplebot: echo I'm a very silly bot
256   <examplebot> I'm a very silly bot
257 #+END_SRC
258
259 Whee, that looks like fun!
260 To implement it, we're going to pull out Guile's pattern matcher.
261
262 #+BEGIN_SRC scheme
263   (define-method (handle-line (irc-bot <my-irc-bot>) message
264                               speaker channel line emote?)
265     (define my-name (irc-bot-username irc-bot))
266     (define (looks-like-me? str)
267       (or (equal? str my-name)
268           (equal? str (string-concatenate (list my-name ":")))))
269     (match (string-split line #\space)
270       (((? looks-like-me? _) action action-args ...)
271        (match action
272          ;; The classic botsnack!
273          ("botsnack"
274           (<- (actor-id irc-bot) 'send-line channel
275               "Yippie! *does a dance!*"))
276          ;; Return greeting
277          ((or "hello" "hello!" "hello." "greetings" "greetings." "greetings!"
278               "hei" "hei." "hei!" "hi" "hi!")
279           (<- (actor-id irc-bot) 'send-line channel
280               (format #f "Oh hi ~a!" speaker)))
281          ("echo"
282           (<- (actor-id irc-bot) 'send-line channel
283               (string-join action-args " ")))
284
285          ;; --->  Add yours here <---
286
287          ;; Default
288          (_
289           (<- (actor-id irc-bot) 'send-line channel
290               "*stupid puppy look*"))))))
291 #+END_SRC
292
293 Parsing the pattern matcher syntax is left as an exercise for the
294 reader.
295
296 If you're getting the sense that we could make this a bit less wordy,
297 you're right:
298
299 #+BEGIN_SRC scheme
300   (define-method (handle-line (irc-bot <my-irc-bot>) message
301                               speaker channel line emote?)
302     (define my-name (irc-bot-username irc-bot))
303     (define (looks-like-me? str)
304       (or (equal? str my-name)
305           (equal? str (string-concatenate (list my-name ":")))))
306     (define (respond respond-line)
307       (<- (actor-id irc-bot) 'send-line channel
308           respond-line))
309     (match (string-split line #\space)
310       (((? looks-like-me? _) action action-args ...)
311        (match action
312          ;; The classic botsnack!
313          ("botsnack"
314           (respond "Yippie! *does a dance!*"))
315          ;; Return greeting
316          ((or "hello" "hello!" "hello." "greetings" "greetings." "greetings!"
317               "hei" "hei." "hei!" "hi" "hi." "hi!")
318           (respond (format #f "Oh hi ~a!" speaker)))
319          ("echo"
320           (respond (string-join action-args " ")))
321
322          ;; --->  Add yours here <---
323
324          ;; Default
325          (_
326           (respond "*stupid puppy look*"))))))
327 #+END_SRC
328
329 Okay, that looks pretty good!
330 Now we have enough information to build an IRC bot that can do a lot
331 of things.
332 Take some time to experiment with extending the bot a bit before
333 moving on to the next section!
334 What cool commands can you add?
335
336 [fn:irc-hacking]
337   In the 1990s I remember stumbling into some funky IRC chat rooms and
338   being astounded that people there had what they called "bots" hanging
339   around.
340   From then until now, I've always enjoyed encountering bots whose range
341   of functionality has spanned from saying absurd things, to taking
342   messages when their "owners" were offline, to reporting the weather,
343   to logging meetings for participants.
344   And it turns out, IRC bots are a great way to cut your teeth on
345   networked programming; since IRC is a fairly simple line-delineated
346   protocol, it's a great way to learn to interact with sockets.
347   (My first IRC bot helped my team pick a place to go to lunch, previously
348   a source of significant dispute!)
349   At the time of writing, venture capital awash startups are trying to
350   turn chatbots into "big business"... a strange (and perhaps absurd)
351   thing given chat bots being a fairly mundane novelty amongst hackers
352   and teenagers everywhere a few decades ago.
353
354 [fn:send-message-provenance]
355   8sync's name for sending a message, "<-", comes from older,
356   early lisp object oriented systems which were, as it turned out,
357   inspired by the actor model!
358   Eventually message passing was dropped in favor of something called
359   "generic functions" or "generic methods"
360   (you may observe we made use of such a thing in extending
361   handle-line).
362   Many lispers believe that there is no need for message passing
363   with generic methods and some advanced functional techniques,
364   but in a concurrent environment message passing becomes useful
365   again, especially when the communicating objects / actors are not
366   in the same address space.
367
368 ** Writing our own actors
369
370 Let's write the most basic, boring actor possible.
371 How about an actor that start sleeping, and keeps sleeping?
372
373 #+BEGIN_SRC scheme
374   (use-modules (oop goops)
375                (8sync))
376
377   (define-class <sleeper> (<actor>)
378     (actions #:allocation #:each-subclass
379              #:init-value (build-actions
380                            (*init* sleeper-loop))))
381
382   (define (sleeper-loop actor message)
383     (while (actor-alive? actor)
384       (display "Zzzzzzzz....\n")
385       ;; Sleep for one second      
386       (8sleep (sleeper-sleep-secs actor))))
387
388   (let* ((hive (make-hive))
389          (sleeper (bootstrap-actor hive <sleeper>)))
390     (run-hive hive '()))
391 #+END_SRC
392
393 We see some particular things in this example.
394 One thing is that our =<sleeper>= actor has an actions slot.
395 This is used to look up what the "action handler" for a message is.
396 We have to set the #:allocation to either =#:each-subclass= or
397 =#:class=.[fn:class-bug]
398
399 The only action handler we've added is for =*init*=, which is called
400 implicitly when the actor first starts up.
401 (This will be true whether we bootstrap the actor before the hive
402 starts or create it during the hive's execution.)
403
404 In our sleeper-loop we also see a call to "8sleep".
405 "8sleep" is like Guile's "sleep" method, except it is non-blocking
406 and will always yield to the scheduler.
407
408 Our while loop also checks "actor-alive?" to see whether or not
409 it is still registered.
410 In general, if you keep a loop in your actor that regularly yields
411 to the scheduler, you should check this.[fn:actor-alive-deprecated-soon]
412 (An alternate way to handle it would be to not use a while loop at all
413 but simply send a message to ourselves with "<-" to call the
414 sleeper-loop handler again.
415 If the actor was dead, the message simply would not be delivered and
416 thus the loop would stop.)
417
418 It turns out we could have written the class for the actor much more
419 simply:
420
421 #+BEGIN_SRC scheme
422   ;; You could do this instead of the define-class above.
423   (define-actor <sleeper> (<actor>)
424     ((*init* sleeper-loop)))
425 #+END_SRC
426
427 This is sugar, and expands into exactly the same thing as the
428 define-class above.
429 The third argument is an argument list, the same as what's passed
430 into build-actions.
431 Everything after that is a slot.
432 So for example, if we had added an optional slot to specify
433 how many seconds to sleep, we could have done it like so:
434
435 #+BEGIN_SRC scheme
436   (define-actor <sleeper> (<actor>)
437     ((*init* sleeper-loop))
438     (sleep-secs #:init-value 1
439                 #:getter sleeper-sleep-secs))
440 #+END_SRC
441
442 This actor is pretty lazy though.
443 Time to get back to work!
444 Let's build a worker / manager type system.
445
446 #+BEGIN_SRC scheme
447   (use-modules (8sync)
448                (oop goops))
449
450   (define-actor <manager> (<actor>)
451     ((assign-task manager-assign-task))
452     (direct-report #:init-keyword #:direct-report
453                    #:getter manager-direct-report))
454
455   (define (manager-assign-task manager message difficulty)
456     "Delegate a task to our direct report"
457     (display "manager> Work on this task for me!\n")
458     (<- (manager-direct-report manager)
459         'work-on-this difficulty))
460 #+END_SRC
461
462 This manager keeps track of a direct report and tells them to start
463 working on a task... simple delegation.
464 Nothing here is really new, but note that our friend "<-" (which means
465 "send message") is back.
466 There's one difference this time... the first time we saw "<-" was in
467 the handle-line procedure of the irc-bot, and in that case we explicitly
468 pulled the actor-id after the actor we were sending the message to
469 (ourselves), which we aren't doing here.
470 But that was an unusual case, because the actor was ourself.
471 In this case, and in general, actors don't have direct references to
472 other actors; instead, all they have is access to identifiers which
473 reference other actors.
474
475 #+BEGIN_SRC scheme
476   (define-actor <worker> (<actor>)
477     ((work-on-this worker-work-on-this))
478     (task-left #:init-keyword #:task-left
479                #:accessor worker-task-left))
480
481   (define (worker-work-on-this worker message difficulty)
482     "Work on one task until done."
483     (set! (worker-task-left worker) difficulty)
484     (display "worker> Whatever you say, boss!\n")
485     (while (and (actor-alive? worker)
486                 (> (worker-task-left worker) 0))
487       (display "worker> *huff puff*\n")
488       (set! (worker-task-left worker)
489             (- (worker-task-left worker) 1))
490       (8sleep (/ 1 3))))
491 #+END_SRC
492
493 The worker also contains familiar code, but we now see that we can
494 call 8sleep with non-integer real numbers.
495
496 Looks like there's nothing left to do but run it.
497
498 #+BEGIN_SRC scheme
499   (let* ((hive (make-hive))
500          (worker (bootstrap-actor hive <worker>))
501          (manager (bootstrap-actor hive <manager>
502                                    #:direct-report worker)))
503     (run-hive hive (list (bootstrap-message hive manager 'assign-task 5))))
504 #+END_SRC
505
506 Unlike the =<sleeper>=, our =<manager>= doesn't have an implicit
507 =*init*= method, so we've bootstrapped the calling =assign-task= action.
508
509 #+BEGIN_SRC text
510 manager> Work on this task for me!
511 worker> Whatever you say, boss!
512 worker> *huff puff*
513 worker> *huff puff*
514 worker> *huff puff*
515 worker> *huff puff*
516 worker> *huff puff*
517 #+END_SRC
518
519 "<-" pays no attention to what happens with the messages it has sent
520 off.
521 This is useful in many cases... we can blast off many messages and
522 continue along without holding anything back.
523
524 But sometimes we want to make sure that something completes before
525 we do something else, or we want to send a message and get some sort
526 of information back.
527 Luckily 8sync comes with an answer to that with "<-wait", which will
528 suspend the caller until the callee gives some sort of response, but
529 which does not block the rest of the program from running.
530 Let's try applying that to our own code by turning our manager
531 into a micromanager.
532
533 #+BEGIN_SRC scheme
534   ;;; Update this method
535   (define (manager-assign-task manager message difficulty)
536     "Delegate a task to our direct report"
537     (display "manager> Work on this task for me!\n")
538     (<- (manager-direct-report manager)
539         'work-on-this difficulty)
540
541     ;; Wait a moment, then call the micromanagement loop
542     (8sleep (/ 1 2))
543     (manager-micromanage-loop manager))
544
545   ;;; And add the following
546   ;;;   (... Note: do not model actual employee management off this)
547   (define (manager-micromanage-loop manager)
548     "Pester direct report until they're done with their task."
549     (display "manager> Are you done yet???\n")
550     (let ((worker-is-done
551            (mbody-val (<-wait (manager-direct-report manager)
552                               'done-yet?))))
553       (if worker-is-done
554           (begin (display "manager> Oh!  I guess you can go home then.\n")
555                  (<- (manager-direct-report manager) 'go-home))
556           (begin (display "manager> Harumph!\n")
557                  (8sleep (/ 1 2))
558                  (when (actor-alive? manager)
559                    (manager-micromanage-loop manager))))))
560 #+END_SRC
561
562 We've appended a micromanagement loop here... but what's going on?
563 "<-wait", as it sounds, waits for a reply, and returns a reply
564 message.
565 In this case there's a value in the body of the message we want,
566 so we pull it out with mbody-val.
567 (It's possible for a remote actor to return multiple values, in which
568 case we'd want to use mbody-receive, but that's a bit more
569 complicated.)
570
571 Of course, we need to update our worker accordingly as well.
572
573 #+BEGIN_SRC scheme
574   ;;; Update the worker to add the following new actions:
575   (define-actor <worker> (<actor>)
576     ((work-on-this worker-work-on-this)
577      ;; Add these:
578      (done-yet? worker-done-yet?)
579      (go-home worker-go-home))
580     (task-left #:init-keyword #:task-left
581                #:accessor worker-task-left))
582
583   ;;; New procedures:
584   (define (worker-done-yet? worker message)
585     "Reply with whether or not we're done yet."
586     (let ((am-i-done? (= (worker-task-left worker) 0)))
587       (if am-i-done?
588           (display "worker> Yes, I finished up!\n")
589           (display "worker> No... I'm still working on it...\n"))
590       (<-reply message am-i-done?)))
591
592   (define (worker-go-home worker message)
593     "It's off of work for us!"
594     (display "worker> Whew!  Free at last.\n")
595     (self-destruct worker))
596 #+END_SRC
597
598 (As you've probably guessed, you wouldn't normally call =display=
599 everywhere as we are in this program... that's just to make the
600 examples more illustrative.)
601
602 "<-reply" is what actually returns the information to the actor
603 waiting on the reply.
604 It takes as an argument the actor sending the message, the message
605 it is in reply to, and the rest of the arguments are the "body" of
606 the message.
607 (If an actor handles a message that is being "waited on" but does not
608 explicitly reply to it, an auto-reply with an empty body will be
609 triggered so that the waiting actor is not left waiting around.)
610
611 The last thing to note is the call to "self-destruct".
612 This does what you might expect: it removes the actor from the hive.
613 No new messages will be sent to it.
614 Ka-poof!
615
616 Running it is the same as before:
617
618 #+BEGIN_SRC scheme
619   (let* ((hive (make-hive))
620          (worker (bootstrap-actor hive <worker>))
621          (manager (bootstrap-actor hive <manager>
622                                    #:direct-report worker)))
623     (run-hive hive (list (bootstrap-message hive manager 'assign-task 5))))
624 #+END_SRC
625
626 But the output is a bit different:
627
628 #+BEGIN_SRC scheme
629 manager> Work on this task for me!
630 worker> Whatever you say, boss!
631 worker> *huff puff*
632 worker> *huff puff*
633 manager> Are you done yet???
634 worker> No... I'm still working on it...
635 manager> Harumph!
636 worker> *huff puff*
637 manager> Are you done yet???
638 worker> *huff puff*
639 worker> No... I'm still working on it...
640 manager> Harumph!
641 worker> *huff puff*
642 manager> Are you done yet???
643 worker> Yes, I finished up!
644 manager> Oh!  I guess you can go home then.
645 worker> Whew!  Free at last.
646 #+END_SRC
647
648 [fn:class-bug]
649   #:class should be fine, except there is [[https://debbugs.gnu.org/cgi/bugreport.cgi?bug=25211][a bug in Guile]] which keeps
650   us from using it for now.
651
652 [fn:actor-alive-deprecated-soon]
653   Or rather, for now you should call =actor-alive?= if your code
654   is looping like this.
655   In the future, after an actor dies, its coroutines will
656   automatically be "canceled".
657
658 ** Writing our own network-enabled actor
659
660 So, you want to write a networked actor!
661 Well, luckily that's pretty easy, especially with all you know so far.
662
663 #+BEGIN_SRC scheme
664   (use-modules (oop goops)
665                (8sync)
666                (ice-9 rdelim)  ; line delineated i/o
667                (ice-9 match))  ; pattern matching
668
669   (define-actor <telcmd> (<actor>)
670     ((*init* telcmd-init)
671      (*cleanup* telcmd-cleanup)
672      (new-client telcmd-new-client)
673      (handle-line telcmd-handle-line))
674     (socket #:accessor telcmd-socket
675             #:init-value #f))
676 #+END_SRC
677
678 Nothing surprising about the actor definition, though we do see that
679 it has a slot for a socket.
680 Unsurprisingly, that will be set up in the =*init*= handler.
681
682 #+BEGIN_SRC scheme
683   (define (set-port-nonblocking! port)
684     (let ((flags (fcntl port F_GETFL)))
685       (fcntl port F_SETFL (logior O_NONBLOCK flags))))
686
687   (define (setup-socket)
688     ;; our socket
689     (define s
690       (socket PF_INET SOCK_STREAM 0))
691     ;; reuse port even if busy
692     (setsockopt s SOL_SOCKET SO_REUSEADDR 1)
693     ;; connect to port 8889 on localhost
694     (bind s AF_INET INADDR_LOOPBACK 8889)
695     ;; make it nonblocking and start listening
696     (set-port-nonblocking! s)
697     (listen s 5)
698     s)
699
700   (define (telcmd-init telcmd message)
701     (set! (telcmd-socket telcmd) (setup-socket))
702     (display "Connect like: telnet localhost 8889\n")
703     (while (actor-alive? telcmd)
704       (let ((client-connection (accept (telcmd-socket telcmd))))
705         (<- (actor-id telcmd) 'new-client client-connection))))
706
707   (define (telcmd-cleanup telcmd message)
708     (display "Closing socket!\n")
709     (when (telcmd-socket telcmd)
710       (close (telcmd-socket telcmd))))
711 #+END_SRC
712
713 That =setup-socket= code looks pretty hard to read!
714 But that's pretty standard code for setting up a socket.
715 One special thing is done though... the call to
716 =set-port-nonblocking!= sets flags on the socket port so that,
717 you guessed it, will be a nonblocking port.
718
719 This is put to immediate use in the telcmd-init method.
720 This code looks suspiciously like it /should/ block... after
721 all, it just keeps looping forever.
722 But since 8sync is using Guile's suspendable ports code feature,
723 so every time this loop hits the =accept= call, if that call
724 /would have/ blocked, instead this whole procedure suspends
725 to the scheduler... automatically!... allowing other code to run.
726
727 So, as soon as we do accept a connection, we send a message to
728 ourselves with the =new-client= action.
729 But wait!
730 Aren't actors only supposed to handle one message at a time?
731 If the telcmd-init loop just keeps on looping and looping,
732 when will the =new-client= message ever be handled?
733 8sync actors only receive one message at a time, but by default if an
734 actor's message handler suspends to the agenda for some reason (such
735 as to send a message or on handling I/O), that actor may continue to
736 accept other messages, but always in the same thread.[fn:queued-handler]
737
738 We also see that we've established a =*cleanup*= handler.
739 This is run any time either the actor dies, either through self
740 destructing, because the hive completes its work, or because
741 a signal was sent to interrupt or terminate our program.
742 In our case, we politely close the socket when =<telcmd>= dies.
743
744 #+BEGIN_SRC scheme
745   (define (telcmd-new-client telcmd message client-connection)
746     (define client (car client-connection))
747     (set-port-nonblocking! client)
748     (let loop ()
749       (let ((line (read-line client)))
750         (cond ((eof-object? line)
751                (close client))
752               (else
753                (<- (actor-id telcmd) 'handle-line
754                    client (string-trim-right line #\return))
755                (when (actor-alive? telcmd)
756                  (loop)))))))
757
758   (define (telcmd-handle-line telcmd message client line)
759     (match (string-split line #\space)
760       (("") #f)  ; ignore empty lines
761       (("time" _ ...)
762        (display
763         (strftime "The time is: %c\n" (localtime (current-time)))
764         client))
765       (("echo" rest ...)
766        (format client "~a\n" (string-join rest " ")))
767       ;; default
768       (_ (display "Sorry, I don't know that command.\n" client))))
769 #+END_SRC
770
771 Okay, we have a client, so we handle it!
772 And once again... we see this goes off on a loop of its own!
773 (Also once again, we have to do the =set-port-nonblocking!= song and
774 dance.)
775 This loop also automatically suspends when it would otherwise block...
776 as long as read-line has information to process, it'll keep going, but
777 if it would have blocked waiting for input, then it would suspend the
778 agenda.[fn:setvbuf]
779
780 The actual method called whenever we have a "line" of input is pretty
781 straightforward... in fact it looks an awful lot like the IRC bot
782 handle-line procedure we used earlier.
783 No surprises there![fn:why-send-a-message-to-handle-line]
784
785 Now let's run it:
786
787 #+BEGIN_SRC scheme
788   (let* ((hive (make-hive))
789          (telcmd (bootstrap-actor hive <telcmd>)))
790     (run-hive hive '()))
791 #+END_SRC
792
793 Open up another terminal... you can connect via telnet:
794
795 #+BEGIN_SRC text
796 $ telnet localhost 8889
797 Trying 127.0.0.1...
798 Connected to localhost.
799 Escape character is '^]'.
800 time
801 The time is: Thu Jan  5 03:20:17 2017
802 echo this is an echo
803 this is an echo
804 shmmmmmmorp
805 Sorry, I don't know that command.
806 #+END_SRC
807
808 Horray, it works!
809 Type =Ctrl+] Ctrl+d= to exit telnet.
810
811 Not so bad!
812 There's more that could be optimized, but we'll consider that to be
813 advanced topics of discussion.
814
815 So that's a pretty solid intro to how 8sync works!
816 Now that you've gone through this introduction, we hope you'll have fun
817 writing and hooking together your own actors.
818 Since actors are so modular, it's easy to have a program that has
819 multiple subystems working together.
820 You could build a worker queue system that displayed a web interface
821 and spat out notifications about when tasks finish to IRC, and making
822 all those actors talk to each other should be a piece of cake.
823 The sky's the limit!
824
825 Happy hacking!
826
827 [fn:setvbuf]
828   If there's a lot of data coming in and you don't want your I/O loop
829   to become too "greedy", take a look at =setvbuf=.
830
831 [fn:queued-handler]
832   This is customizable: an actor can be set up to queue messages so
833   that absolutely no messages are handled until the actor completely
834   finishes handling one message.
835   Our loop couldn't look quite like this though!
836
837 [fn:why-send-a-message-to-handle-line]
838   Well, there may be one surprise to a careful observer.
839   Why are we sending a message to ourselves?
840   Couldn't we have just dropped the argument of "message" to
841   telcmd-handle-line and just called it like any other procedure?
842   Indeed, we /could/ do that, but sending a message to ourself has
843   an added advantage: if we accidentally "break" the
844   telcmd-handle-line procedure in some way (say we add a fun new
845   command we're playing with it), raising an exception won't break
846   and disconnect the client's main loop, it'll just break the
847   message handler for that one line, and our telcmd will happily
848   chug along accepting another command from the user while we try
849   to figure out what happened to the last one.
850
851 ** An intermission on live hacking
852
853 This section is optional, but highly recommended.
854 It requires that you're a user of GNU Emacs.
855 If you aren't, don't worry... you can forge ahead and come back in case
856 you ever do become an Emacs user.
857 (If you're more familiar with Vi/Vim style editing, I hear good things
858 about Spacemacs...)
859
860 Remember all the way back when we were working on the IRC bot?
861 So you may have noticed while updating that section that the
862 start/stop cycle of hacking isn't really ideal.
863 You might either edit a file in your editor, then run it, or
864 type the whole program into the REPL, but then you'll have to spend
865 extra time copying it to a file.
866 Wouldn't it be nice if it were possible to both write code in a
867 file and try it as you go?
868 And wouldn't it be even better if you could live edit a program
869 while it's running?
870
871 Luckily, there's a great Emacs mode called Geiser which makes
872 editing and hacking and experimenting all happen in harmony.
873 And even better, 8sync is optimized for this experience.
874 8sync provides easy drop-in "cooperative REPL" support, and
875 most code can be simply redefined on the fly in 8sync through Geiser
876 and actors will immediately update their behavior, so you can test
877 and tweak things as you go.
878
879 Okay, enough talking.  Let's add it!
880 Redefine run-bot like so:
881
882 #+BEGIN_SRC scheme
883   (define* (run-bot #:key (username "examplebot")
884                     (server "irc.freenode.net")
885                     (channels '("##botchat"))
886                     (repl-path "/tmp/8sync-repl"))
887     (define hive (make-hive))
888     (define irc-bot
889       (bootstrap-actor hive <my-irc-bot>
890                        #:username username
891                        #:server server
892                        #:channels channels))
893     (define repl-manager
894       (bootstrap-actor hive <repl-manager>
895                        #:path repl-path))
896
897     (run-hive hive '()))
898 #+END_SRC
899
900 If we put a call to run-bot at the bottom of our file we can call it,
901 and the repl-manager will start something we can connect to automatically.
902 Horray!
903 Now when we run this it'll start up a REPL with a unix domain socket at
904 the repl-path.
905 We can connect to it in emacs like so:
906
907 : M-x geiser-connect-local <RET> guile <RET> /tmp/8sync-repl <RET>
908
909 Okay, so what does this get us?
910 Well, we can now live edit our program.
911 Let's change how our bot behaves a bit.
912 Let's change handle-line and tweak how the bot responds to a botsnack.
913 Change this part:
914
915 #+BEGIN_SRC scheme
916   ;; From this:
917   ("botsnack"
918    (respond "Yippie! *does a dance!*"))
919
920   ;; To this:
921   ("botsnack"
922    (respond "Yippie! *catches botsnack in midair!*"))
923 #+END_SRC
924
925 Okay, now let's evaluate the change of the definition.
926 You can hit "C-M-x" anywhere in the definition to re-evaluate.
927 (You can also position your cursor at the end of the definition and press
928 "C-x C-e", but I've come to like "C-M-x" better because I can evaluate as soon
929 as I'm done writing.)
930 Now, on IRC, ask your bot for a botsnack.
931 The bot should give the new message... with no need to stop and start the
932 program!
933
934 Let's fix a bug live.
935 Our current program works great if you talk to your bot in the same
936 IRC channel, but what if you try to talk to them over private message?
937
938 #+BEGIN_SRC text
939 IRC> /query examplebot
940 <foo-user> examplebot: hi!
941 #+END_SRC
942
943 Hm, we aren't seeing any response on IRC!
944 Huh?  What's going on?
945 It's time to do some debugging.
946 There are plenty of debugging tools in Guile, but sometimes the simplest
947 is the nicest, and the simplest debugging route around is good old
948 fashioned print debugging.
949
950 It turns out Guile has an under-advertised feature which makes print
951 debugging really easy called "pk", pronounced "peek".
952 What pk accepts a list of arguments, prints out the whole thing,
953 but returns the last argument.
954 This makes wrapping bits of our code pretty easy to see what's
955 going on.
956 So let's peek into our program with pk.
957 Edit the respond section to see what channel it's really sending
958 things to:
959
960 #+BEGIN_SRC scheme
961   (define-method (handle-line (irc-bot <my-irc-bot>) message
962                               speaker channel line emote?)
963     ;; [... snip ...]
964     (define (respond respond-line)
965       (<- (actor-id irc-bot) 'send-line (pk 'channel channel)
966           respond-line))
967     ;; [... snip ...]
968     )
969 #+END_SRC
970
971 Re-evaluate.
972 Now let's ping our bot in both the channel and over PM.
973
974 #+BEGIN_SRC text
975 ;;; (channel "##botchat")
976
977 ;;; (channel "sinkbot")
978 #+END_SRC
979
980 Oh okay, this makes sense.
981 When we're talking in a normal multi-user channel, the channel we see
982 the message coming from is the same one we send to.
983 But over PM, the channel is a username, and in this case the username
984 we're sending our line of text to is ourselves.
985 That isn't what we want.
986 Let's edit our code so that if we see that the channel we're sending
987 to looks like our own username that we respond back to the sender.
988 (We can remove the pk now that we know what's going on.)
989
990 #+BEGIN_SRC scheme
991   (define-method (handle-line (irc-bot <my-irc-bot>) message
992                               speaker channel line emote?)
993     ;; [... snip ...]
994     (define (respond respond-line)
995       (<- (actor-id irc-bot) 'send-line
996           (if (looks-like-me? channel)
997               speaker    ; PM session
998               channel)   ; normal IRC channel
999           respond-line))
1000     ;; [... snip ...]
1001     )
1002 #+END_SRC
1003
1004 Re-evaluate and test.
1005
1006 #+BEGIN_SRC text
1007 IRC> /query examplebot
1008 <foo-user> examplebot: hi!
1009 <examplebot> Oh hi foo-user!
1010 #+END_SRC
1011
1012 Horray!
1013
1014
1015 * API reference
1016
1017 * Systems reference
1018 ** IRC
1019 ** Web / HTTP
1020 ** COMMENT Websockets
1021
1022 * Addendum
1023 ** Recommended .emacs additions
1024
1025 In order for =mbody-receive= to indent properly, put this in your
1026 .emacs:
1027
1028 #+BEGIN_SRC emacs-lisp
1029 (put 'mbody-receive 'scheme-indent-function 2)
1030 #+END_SRC
1031
1032 ** 8sync and Fibers
1033
1034 One other major library for asynchronous communication in Guile-land
1035 is [[https://github.com/wingo/fibers/][Fibers]].
1036 There's a lot of overlap:
1037
1038  - Both use Guile's suspendable-ports facility
1039  - Both communicate between asynchronous processes using message passing;
1040    you don't have to squint hard to see the relationship between Fibers'
1041    channels and 8sync's actor inboxes.
1042
1043 However, there are clearly differences too.
1044 There's a one to one relationship between 8sync actors and an actor inbox,
1045 whereas each Fibers fiber may read from multiple channels, for example.
1046
1047 Luckily, it turns out there's a clear relationship, based on real,
1048 actual theory!
1049 8sync is based on the [[https://en.wikipedia.org/wiki/Actor_model][actor model]] whereas fibers follows
1050 [[http://usingcsp.com/][Communicating Sequential Processes (CSP)]], which is a form of
1051 [[https://en.wikipedia.org/wiki/Process_calculus][process calculi]]. 
1052 And it turns out, the
1053 [[https://en.wikipedia.org/wiki/Actor_model_and_process_calculi][relationship between the actor model and process calculi]] is well documented,
1054 and even more precisely, the
1055 [[https://en.wikipedia.org/wiki/Communicating_sequential_processes#Comparison_with_the_Actor_Model][relationship between CSP and the actor model]] is well understood too.
1056
1057 So, 8sync and Fibers do take somewhat different approaches, but both
1058 have a solid theoretical backing... and their theories are well
1059 understood in terms of each other.
1060 Good news for theory nerds!
1061
1062 (Since the actors and CSP are [[https://en.wikipedia.org/wiki/Dual_%28mathematics%29][dual]], maybe eventually 8sync will be
1063 implemented on top of Fibers... that remains to be seen!)
1064