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