+++ /dev/null
-# Permission is granted to copy, distribute and/or modify this document
-# under the terms of the GNU Free Documentation License, Version 1.3
-# or any later version published by the Free Software Foundation;
-# with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
-# A copy of the license is included in the section entitled ``GNU
-# Free Documentation License''.
-#
-# A copy of the license is also available from the Free Software
-# Foundation Web site at http://www.gnu.org/licenses/fdl.html
-#
-# Alternately, this document is also available under the Lesser General
-# Public License, version 3 or later, as published by the Free Software
-# Foundation.
-#
-# A copy of the license is also available from the Free Software
-# Foundation Web site at http://www.gnu.org/licenses/lgpl.html
-
-* Preface
-
-Welcome to 8sync's documentation!
-8sync is an asynchronous programming environment for GNU Guile.
-(Get it? 8sync? Async??? Quiet your groans, it's a great name!)
-
-8sync has some nice properties:
-
- - 8sync uses the actor model as its fundamental concurrency
- synchronization mechanism.
- Since the actor model is a "shared nothing" asynchronous
- environment, you don't need to worry about deadlocks or other
- tricky problems common to other asynchronous models.
- Actors are modular units of code and state which communicate
- by sending messages to each other.
- - If you've done enough asynchronous programming, you're probably
- familiar with the dreaded term "callback hell".
- Getting around callback hell usually involves a tradeoff of other,
- still rather difficult to wrap your brain around programming
- patterns.
- 8sync uses some clever tricks involving "delimited continuations"
- under the hood to make the code you write look familiar and
- straightforward.
- When you need to send a request to another actor and get some
- information back from it without blocking, there's no need
- to write a separate procedure... 8sync's scheduler will suspend
- your procedure and wake it back up when a response is ready.
- - Even nonblocking I/O code is straightforward to write.
- Thanks to the "suspendable ports" code introduced in Guile 2.2,
- writing asynchronous, nonblocking networked code looks mostly
- like writing the same synchronous code.
- 8sync's scheduler handles suspending and resuming networked
- code that would otherwise block.
- - 8sync aims to be "batteries included".
- Useful subsystems for IRC bots, HTTP servers, and so on are
- included out of the box.
- - 8sync prioritizes live hacking.
- If using an editor like Emacs with a nice mode like Geiser,
- an 8sync-using developer can change and fine-tune the behavior
- of code /while it runs/.
- This makes both debugging and development much more natural,
- allowing the right designs to evolve under your fingertips.
- A productive hacker is a happy hacker, after all!
-
-In the future, 8sync will also provide the ability to spawn and
-communicate with actors on different threads, processes, and machines,
-with most code running the same as if actors were running in the same
-execution environment.
-
-But as a caution, 8sync is still very young.
-The API is stabilizing, but not yet stable, and it is not yet well
-"battle-tested".
-Hacker beware!
-But, consider this as much an opportunity as a warning.
-8sync is in a state where there is much room for feedback and
-contributions.
-Your help wanted!
-
-And now, into the wild, beautiful frontier.
-Onward!
-
-* Tutorial
-
-** A silly little IRC bot
-
-IRC! Internet Relay Chat!
-The classic chat protocol of the Internet.
-And it turns out, one of the best places to learn about networked
-programming.[fn:irc-hacking]
-We ourselves are going to explore chat bots as a basis for getting our
-feet wet in 8sync.
-
-First of all, we're going to need to import some modules. Put this at
-the top of your file:
-
-#+BEGIN_SRC scheme
- (use-modules (8sync) ; 8sync's agenda and actors
- (8sync systems irc) ; the irc bot subsystem
- (oop goops) ; 8sync's actors use GOOPS
- (ice-9 format) ; basic string formatting
- (ice-9 match)) ; pattern matching
-#+END_SRC
-
-Now we need to add our bot. Initially, it won't do much.
-
-#+BEGIN_SRC scheme
- (define-class <my-irc-bot> (<irc-bot>))
-
- (define-method (handle-line (irc-bot <my-irc-bot>) message
- speaker channel line emote?)
- (if emote?
- (format #t "~a emoted ~s in channel ~a\n"
- speaker line channel)
- (format #t "~a said ~s in channel ~a\n"
- speaker line channel)))
-#+END_SRC
-
-We've just defined our own IRC bot!
-This is an 8sync actor.
-(8sync uses GOOPS to define actors.)
-We extended the handle-line generic method, so this is the code that
-will be called whenever the IRC bot "hears" anything.
-This method is itself an action handler, hence the second argument
-for =message=, which we can ignore for now.
-Pleasantly, the message's argument body is passed in as the rest of
-the arguments.
-
-For now the code is pretty basic: it just outputs whatever it "hears"
-from a user in a channel to the current output port.
-Pretty boring!
-But it should help us make sure we have things working when we kick
-things off.
-
-Speaking of, even though we've defined our actor, it's not running
-yet. Time to fix that!
-
-#+BEGIN_SRC scheme
-(define* (run-bot #:key (username "examplebot")
- (server "irc.freenode.net")
- (channels '("##botchat")))
- (define hive (make-hive))
- (define irc-bot
- (bootstrap-actor hive <my-irc-bot>
- #:username username
- #:server server
- #:channels channels))
- (run-hive hive '()))
-#+END_SRC
-
-Actors are connected to something called a "hive", which is a
-special kind of actor that runs and manages all the other actors.
-Actors can spawn other actors, but before we start the hive we use
-this special =bootstrap-actor= method.
-It takes the hive as its first argument, the actor class as the second
-argument, and the rest are initialization arguments to the
-actor.
-=bootstrap-actor= passes back not the actor itself (we don't
-get access to that usually) but the *id* of the actor.
-(More on this later.)
-Finally we run the hive with run-hive and pass it a list of
-"bootstrapped" messages.
-Normally actors send messages to each other (and sometimes themselves),
-but we need to send a message or messages to start things or else
-nothing is going to happen.
-
-We can run it like:
-
-#+BEGIN_SRC scheme
-(run-bot #:username "some-bot-name") ; be creative!
-#+END_SRC
-
-Assuming all the tubes on the internet are properly connected, you
-should be able to join the "##botchat" channel on irc.freenode.net and
-see your bot join as well.
-Now, as you probably guessed, you can't really /do/ much yet.
-If you talk to the bot, it'll send messages to the terminal informing
-you as such, but it's hardly a chat bot if it's not chatting yet.
-
-So let's do the most boring (and annoying) thing possible.
-Let's get it to echo whatever we say back to us.
-Change handle-line to this:
-
-#+BEGIN_SRC scheme
- (define-method (handle-line (irc-bot <my-irc-bot>) message
- speaker channel line emote?)
- (<- (actor-id irc-bot) 'send-line channel
- (format #f "Bawwwwk! ~a says: ~a" speaker line)))
-#+END_SRC
-
-This will do exactly what it looks like: repeat back whatever anyone
-says like an obnoxious parrot.
-Give it a try, but don't keep it running for too long... this
-bot is so annoying it's likely to get banned from whatever channel
-you put it in.
-
-This method handler does have the advantage of being simple though.
-It introduces a new concept simply... sending a message!
-Whenever you see "<-", you can think of that as saying "send this
-message".
-The arguments to "<-" are as follows: the actor sending the message,
-the id of the actor the message is being sent to, the "action" we
-want to invoke (a symbol), and the rest are arguments to the
-"action handler" which is in this case send-line (with itself takes
-two arguments: the channel our bot should send a message to, and
-the line we want it to spit out to the channel).[fn:send-message-provenance]
-
-Normally in the actor model, we don't have direct references to
-an actor, only an identifier.
-This is for two reasons: to quasi-enforce the "shared nothing"
-environment (actors absolutely control their own resources, and
-"all you can do is send a message" to request that they modify
-them) and because... well, you don't even know where that actor is!
-Actors can be anything, and anywhere.
-It's possible in 8sync to have an actor on a remote hive, which means
-the actor could be on a remote process or even remote machine, and
-in most cases message passing will look exactly the same.
-(There are some exceptions; it's possible for two actors on the same
-hive to "hand off" some special types of data that can't be serialized
-across processes or the network, eg a socket or a closure, perhaps even
-one with mutable state.
-This must be done with care, and the actors should be careful both
-to ensure that they are both local and that the actor handing things
-off no longer accesses that value to preserve the actor model.
-But this is an advanced topic, and we are getting ahead of ourselves.)
-We have to supply the id of the receiving actor, and usually we'd have
-only the identifier.
-But since in this case, since the actor we're sending this to is
-ourselves, we have to pass in our identifier, since the Hive won't
-deliver to anything other than an address.
-
-Astute readers may observe, since this is a case where we are just
-referencing our own object, couldn't we just call "sending a line"
-as a method of our own object without all the message passing?
-Indeed, we do have such a method, so we /could/ rewrite handle-line
-like so:
-
-#+BEGIN_SRC scheme
- (define-method (handle-line (irc-bot <my-irc-bot>) message
- speaker channel line emote?)
- (irc-bot-send-line irc-bot channel
- (format #f "Bawwwwk! ~a says: ~a" speaker line)))
-#+END_SRC
-
-... but we want to get you comfortable and familiar with message
-passing, and we'll be making use of this same message passing shortly
-so that /other/ actors may participate in communicating with IRC
-through our IRC bot.
-
-Anyway, our current message handler is simply too annoying.
-What we would really like to do is have our bot respond to individual
-"commands" like this:
-
-#+BEGIN_SRC text
- <foo-user> examplebot: hi!
- <examplebot> Oh hi foo-user!
- <foo-user> examplebot: botsnack
- <examplebot> Yippie! *does a dance!*
- <foo-user> examplebot: echo I'm a very silly bot
- <examplebot> I'm a very silly bot
-#+END_SRC
-
-Whee, that looks like fun!
-To implement it, we're going to pull out Guile's pattern matcher.
-
-#+BEGIN_SRC scheme
- (define-method (handle-line (irc-bot <my-irc-bot>) message
- speaker channel line emote?)
- (define my-name (irc-bot-username irc-bot))
- (define (looks-like-me? str)
- (or (equal? str my-name)
- (equal? str (string-concatenate (list my-name ":")))))
- (match (string-split line #\space)
- (((? looks-like-me? _) action action-args ...)
- (match action
- ;; The classic botsnack!
- ("botsnack"
- (<- (actor-id irc-bot) 'send-line channel
- "Yippie! *does a dance!*"))
- ;; Return greeting
- ((or "hello" "hello!" "hello." "greetings" "greetings." "greetings!"
- "hei" "hei." "hei!" "hi" "hi!")
- (<- (actor-id irc-bot) 'send-line channel
- (format #f "Oh hi ~a!" speaker)))
- ("echo"
- (<- (actor-id irc-bot) 'send-line channel
- (string-join action-args " ")))
-
- ;; ---> Add yours here <---
-
- ;; Default
- (_
- (<- (actor-id irc-bot) 'send-line channel
- "*stupid puppy look*"))))))
-#+END_SRC
-
-Parsing the pattern matcher syntax is left as an exercise for the
-reader.
-
-If you're getting the sense that we could make this a bit less wordy,
-you're right:
-
-#+BEGIN_SRC scheme
- (define-method (handle-line (irc-bot <my-irc-bot>) message
- speaker channel line emote?)
- (define my-name (irc-bot-username irc-bot))
- (define (looks-like-me? str)
- (or (equal? str my-name)
- (equal? str (string-concatenate (list my-name ":")))))
- (define (respond respond-line)
- (<- (actor-id irc-bot) 'send-line channel
- respond-line))
- (match (string-split line #\space)
- (((? looks-like-me? _) action action-args ...)
- (match action
- ;; The classic botsnack!
- ("botsnack"
- (respond "Yippie! *does a dance!*"))
- ;; Return greeting
- ((or "hello" "hello!" "hello." "greetings" "greetings." "greetings!"
- "hei" "hei." "hei!" "hi" "hi." "hi!")
- (respond (format #f "Oh hi ~a!" speaker)))
- ("echo"
- (respond (string-join action-args " ")))
-
- ;; ---> Add yours here <---
-
- ;; Default
- (_
- (respond "*stupid puppy look*"))))))
-#+END_SRC
-
-Okay, that looks pretty good!
-Now we have enough information to build an IRC bot that can do a lot
-of things.
-Take some time to experiment with extending the bot a bit before
-moving on to the next section!
-What cool commands can you add?
-
-[fn:irc-hacking]
- In the 1990s I remember stumbling into some funky IRC chat rooms and
- being astounded that people there had what they called "bots" hanging
- around.
- From then until now, I've always enjoyed encountering bots whose range
- of functionality has spanned from saying absurd things, to taking
- messages when their "owners" were offline, to reporting the weather,
- to logging meetings for participants.
- And it turns out, IRC bots are a great way to cut your teeth on
- networked programming; since IRC is a fairly simple line-delineated
- protocol, it's a great way to learn to interact with sockets.
- (My first IRC bot helped my team pick a place to go to lunch, previously
- a source of significant dispute!)
- At the time of writing, venture capital awash startups are trying to
- turn chatbots into "big business"... a strange (and perhaps absurd)
- thing given chat bots being a fairly mundane novelty amongst hackers
- and teenagers everywhere a few decades ago.
-
-[fn:send-message-provenance]
- 8sync's name for sending a message, "<-", comes from older,
- early lisp object oriented systems which were, as it turned out,
- inspired by the actor model!
- Eventually message passing was dropped in favor of something called
- "generic functions" or "generic methods"
- (you may observe we made use of such a thing in extending
- handle-line).
- Many lispers believe that there is no need for message passing
- with generic methods and some advanced functional techniques,
- but in a concurrent environment message passing becomes useful
- again, especially when the communicating objects / actors are not
- in the same address space.
-
-** Writing our own actors
-
-Let's write the most basic, boring actor possible.
-How about an actor that start sleeping, and keeps sleeping?
-
-#+BEGIN_SRC scheme
- (use-modules (oop goops)
- (8sync))
-
- (define-class <sleeper> (<actor>)
- (actions #:allocation #:each-subclass
- #:init-value (build-actions
- (*init* sleeper-loop))))
-
- (define (sleeper-loop actor message)
- (while (actor-alive? actor)
- (display "Zzzzzzzz....\n")
- ;; Sleep for one second
- (8sleep (sleeper-sleep-secs actor))))
-
- (let* ((hive (make-hive))
- (sleeper (bootstrap-actor hive <sleeper>)))
- (run-hive hive '()))
-#+END_SRC
-
-We see some particular things in this example.
-One thing is that our =<sleeper>= actor has an actions slot.
-This is used to look up what the "action handler" for a message is.
-We have to set the #:allocation to either =#:each-subclass= or
-=#:class=.[fn:class-bug]
-
-The only action handler we've added is for =*init*=, which is called
-implicitly when the actor first starts up.
-(This will be true whether we bootstrap the actor before the hive
-starts or create it during the hive's execution.)
-
-In our sleeper-loop we also see a call to "8sleep".
-"8sleep" is like Guile's "sleep" method, except it is non-blocking
-and will always yield to the scheduler.
-
-Our while loop also checks "actor-alive?" to see whether or not
-it is still registered.
-In general, if you keep a loop in your actor that regularly yields
-to the scheduler, you should check this.[fn:actor-alive-deprecated-soon]
-(An alternate way to handle it would be to not use a while loop at all
-but simply send a message to ourselves with "<-" to call the
-sleeper-loop handler again.
-If the actor was dead, the message simply would not be delivered and
-thus the loop would stop.)
-
-It turns out we could have written the class for the actor much more
-simply:
-
-#+BEGIN_SRC scheme
- ;; You could do this instead of the define-class above.
- (define-actor <sleeper> (<actor>)
- ((*init* sleeper-loop)))
-#+END_SRC
-
-This is sugar, and expands into exactly the same thing as the
-define-class above.
-The third argument is an argument list, the same as what's passed
-into build-actions.
-Everything after that is a slot.
-So for example, if we had added an optional slot to specify
-how many seconds to sleep, we could have done it like so:
-
-#+BEGIN_SRC scheme
- (define-actor <sleeper> (<actor>)
- ((*init* sleeper-loop))
- (sleep-secs #:init-value 1
- #:getter sleeper-sleep-secs))
-#+END_SRC
-
-This actor is pretty lazy though.
-Time to get back to work!
-Let's build a worker / manager type system.
-
-#+BEGIN_SRC scheme
- (use-modules (8sync)
- (oop goops))
-
- (define-actor <manager> (<actor>)
- ((assign-task manager-assign-task))
- (direct-report #:init-keyword #:direct-report
- #:getter manager-direct-report))
-
- (define (manager-assign-task manager message difficulty)
- "Delegate a task to our direct report"
- (display "manager> Work on this task for me!\n")
- (<- (manager-direct-report manager)
- 'work-on-this difficulty))
-#+END_SRC
-
-This manager keeps track of a direct report and tells them to start
-working on a task... simple delegation.
-Nothing here is really new, but note that our friend "<-" (which means
-"send message") is back.
-There's one difference this time... the first time we saw "<-" was in
-the handle-line procedure of the irc-bot, and in that case we explicitly
-pulled the actor-id after the actor we were sending the message to
-(ourselves), which we aren't doing here.
-But that was an unusual case, because the actor was ourself.
-In this case, and in general, actors don't have direct references to
-other actors; instead, all they have is access to identifiers which
-reference other actors.
-
-#+BEGIN_SRC scheme
- (define-actor <worker> (<actor>)
- ((work-on-this worker-work-on-this))
- (task-left #:init-keyword #:task-left
- #:accessor worker-task-left))
-
- (define (worker-work-on-this worker message difficulty)
- "Work on one task until done."
- (set! (worker-task-left worker) difficulty)
- (display "worker> Whatever you say, boss!\n")
- (while (and (actor-alive? worker)
- (> (worker-task-left worker) 0))
- (display "worker> *huff puff*\n")
- (set! (worker-task-left worker)
- (- (worker-task-left worker) 1))
- (8sleep (/ 1 3))))
-#+END_SRC
-
-The worker also contains familiar code, but we now see that we can
-call 8sleep with non-integer real numbers.
-
-Looks like there's nothing left to do but run it.
-
-#+BEGIN_SRC scheme
- (let* ((hive (make-hive))
- (worker (bootstrap-actor hive <worker>))
- (manager (bootstrap-actor hive <manager>
- #:direct-report worker)))
- (run-hive hive (list (bootstrap-message hive manager 'assign-task 5))))
-#+END_SRC
-
-Unlike the =<sleeper>=, our =<manager>= doesn't have an implicit
-=*init*= method, so we've bootstrapped the calling =assign-task= action.
-
-#+BEGIN_SRC text
-manager> Work on this task for me!
-worker> Whatever you say, boss!
-worker> *huff puff*
-worker> *huff puff*
-worker> *huff puff*
-worker> *huff puff*
-worker> *huff puff*
-#+END_SRC
-
-"<-" pays no attention to what happens with the messages it has sent
-off.
-This is useful in many cases... we can blast off many messages and
-continue along without holding anything back.
-
-But sometimes we want to make sure that something completes before
-we do something else, or we want to send a message and get some sort
-of information back.
-Luckily 8sync comes with an answer to that with "<-wait", which will
-suspend the caller until the callee gives some sort of response, but
-which does not block the rest of the program from running.
-Let's try applying that to our own code by turning our manager
-into a micromanager.
-
-#+BEGIN_SRC scheme
- ;;; Update this method
- (define (manager-assign-task manager message difficulty)
- "Delegate a task to our direct report"
- (display "manager> Work on this task for me!\n")
- (<- (manager-direct-report manager)
- 'work-on-this difficulty)
-
- ;; Wait a moment, then call the micromanagement loop
- (8sleep (/ 1 2))
- (manager-micromanage-loop manager))
-
- ;;; And add the following
- ;;; (... Note: do not model actual employee management off this)
- (define (manager-micromanage-loop manager)
- "Pester direct report until they're done with their task."
- (display "manager> Are you done yet???\n")
- (let ((worker-is-done
- (mbody-val (<-wait (manager-direct-report manager)
- 'done-yet?))))
- (if worker-is-done
- (begin (display "manager> Oh! I guess you can go home then.\n")
- (<- (manager-direct-report manager) 'go-home))
- (begin (display "manager> Harumph!\n")
- (8sleep (/ 1 2))
- (when (actor-alive? manager)
- (manager-micromanage-loop manager))))))
-#+END_SRC
-
-We've appended a micromanagement loop here... but what's going on?
-"<-wait", as it sounds, waits for a reply, and returns a reply
-message.
-In this case there's a value in the body of the message we want,
-so we pull it out with mbody-val.
-(It's possible for a remote actor to return multiple values, in which
-case we'd want to use mbody-receive, but that's a bit more
-complicated.)
-
-Of course, we need to update our worker accordingly as well.
-
-#+BEGIN_SRC scheme
- ;;; Update the worker to add the following new actions:
- (define-actor <worker> (<actor>)
- ((work-on-this worker-work-on-this)
- ;; Add these:
- (done-yet? worker-done-yet?)
- (go-home worker-go-home))
- (task-left #:init-keyword #:task-left
- #:accessor worker-task-left))
-
- ;;; New procedures:
- (define (worker-done-yet? worker message)
- "Reply with whether or not we're done yet."
- (let ((am-i-done? (= (worker-task-left worker) 0)))
- (if am-i-done?
- (display "worker> Yes, I finished up!\n")
- (display "worker> No... I'm still working on it...\n"))
- (<-reply message am-i-done?)))
-
- (define (worker-go-home worker message)
- "It's off of work for us!"
- (display "worker> Whew! Free at last.\n")
- (self-destruct worker))
-#+END_SRC
-
-(As you've probably guessed, you wouldn't normally call =display=
-everywhere as we are in this program... that's just to make the
-examples more illustrative.)
-
-"<-reply" is what actually returns the information to the actor
-waiting on the reply.
-It takes as an argument the actor sending the message, the message
-it is in reply to, and the rest of the arguments are the "body" of
-the message.
-(If an actor handles a message that is being "waited on" but does not
-explicitly reply to it, an auto-reply with an empty body will be
-triggered so that the waiting actor is not left waiting around.)
-
-The last thing to note is the call to "self-destruct".
-This does what you might expect: it removes the actor from the hive.
-No new messages will be sent to it.
-Ka-poof!
-
-Running it is the same as before:
-
-#+BEGIN_SRC scheme
- (let* ((hive (make-hive))
- (worker (bootstrap-actor hive <worker>))
- (manager (bootstrap-actor hive <manager>
- #:direct-report worker)))
- (run-hive hive (list (bootstrap-message hive manager 'assign-task 5))))
-#+END_SRC
-
-But the output is a bit different:
-
-#+BEGIN_SRC scheme
-manager> Work on this task for me!
-worker> Whatever you say, boss!
-worker> *huff puff*
-worker> *huff puff*
-manager> Are you done yet???
-worker> No... I'm still working on it...
-manager> Harumph!
-worker> *huff puff*
-manager> Are you done yet???
-worker> *huff puff*
-worker> No... I'm still working on it...
-manager> Harumph!
-worker> *huff puff*
-manager> Are you done yet???
-worker> Yes, I finished up!
-manager> Oh! I guess you can go home then.
-worker> Whew! Free at last.
-#+END_SRC
-
-[fn:class-bug]
- #:class should be fine, except there is [[https://debbugs.gnu.org/cgi/bugreport.cgi?bug=25211][a bug in Guile]] which keeps
- us from using it for now.
-
-[fn:actor-alive-deprecated-soon]
- Or rather, for now you should call =actor-alive?= if your code
- is looping like this.
- In the future, after an actor dies, its coroutines will
- automatically be "canceled".
-
-** Writing our own network-enabled actor
-
-So, you want to write a networked actor!
-Well, luckily that's pretty easy, especially with all you know so far.
-
-#+BEGIN_SRC scheme
- (use-modules (oop goops)
- (8sync)
- (ice-9 rdelim) ; line delineated i/o
- (ice-9 match)) ; pattern matching
-
- (define-actor <telcmd> (<actor>)
- ((*init* telcmd-init)
- (*cleanup* telcmd-cleanup)
- (new-client telcmd-new-client)
- (handle-line telcmd-handle-line))
- (socket #:accessor telcmd-socket
- #:init-value #f))
-#+END_SRC
-
-Nothing surprising about the actor definition, though we do see that
-it has a slot for a socket.
-Unsurprisingly, that will be set up in the =*init*= handler.
-
-#+BEGIN_SRC scheme
- (define (set-port-nonblocking! port)
- (let ((flags (fcntl port F_GETFL)))
- (fcntl port F_SETFL (logior O_NONBLOCK flags))))
-
- (define (setup-socket)
- ;; our socket
- (define s
- (socket PF_INET SOCK_STREAM 0))
- ;; reuse port even if busy
- (setsockopt s SOL_SOCKET SO_REUSEADDR 1)
- ;; connect to port 8889 on localhost
- (bind s AF_INET INADDR_LOOPBACK 8889)
- ;; make it nonblocking and start listening
- (set-port-nonblocking! s)
- (listen s 5)
- s)
-
- (define (telcmd-init telcmd message)
- (set! (telcmd-socket telcmd) (setup-socket))
- (display "Connect like: telnet localhost 8889\n")
- (while (actor-alive? telcmd)
- (let ((client-connection (accept (telcmd-socket telcmd))))
- (<- (actor-id telcmd) 'new-client client-connection))))
-
- (define (telcmd-cleanup telcmd message)
- (display "Closing socket!\n")
- (when (telcmd-socket telcmd)
- (close (telcmd-socket telcmd))))
-#+END_SRC
-
-That =setup-socket= code looks pretty hard to read!
-But that's pretty standard code for setting up a socket.
-One special thing is done though... the call to
-=set-port-nonblocking!= sets flags on the socket port so that,
-you guessed it, will be a nonblocking port.
-
-This is put to immediate use in the telcmd-init method.
-This code looks suspiciously like it /should/ block... after
-all, it just keeps looping forever.
-But since 8sync is using Guile's suspendable ports code feature,
-so every time this loop hits the =accept= call, if that call
-/would have/ blocked, instead this whole procedure suspends
-to the scheduler... automatically!... allowing other code to run.
-
-So, as soon as we do accept a connection, we send a message to
-ourselves with the =new-client= action.
-But wait!
-Aren't actors only supposed to handle one message at a time?
-If the telcmd-init loop just keeps on looping and looping,
-when will the =new-client= message ever be handled?
-8sync actors only receive one message at a time, but by default if an
-actor's message handler suspends to the agenda for some reason (such
-as to send a message or on handling I/O), that actor may continue to
-accept other messages, but always in the same thread.[fn:queued-handler]
-
-We also see that we've established a =*cleanup*= handler.
-This is run any time either the actor dies, either through self
-destructing, because the hive completes its work, or because
-a signal was sent to interrupt or terminate our program.
-In our case, we politely close the socket when =<telcmd>= dies.
-
-#+BEGIN_SRC scheme
- (define (telcmd-new-client telcmd message client-connection)
- (define client (car client-connection))
- (set-port-nonblocking! client)
- (let loop ()
- (let ((line (read-line client)))
- (cond ((eof-object? line)
- (close client))
- (else
- (<- (actor-id telcmd) 'handle-line
- client (string-trim-right line #\return))
- (when (actor-alive? telcmd)
- (loop)))))))
-
- (define (telcmd-handle-line telcmd message client line)
- (match (string-split line #\space)
- (("") #f) ; ignore empty lines
- (("time" _ ...)
- (display
- (strftime "The time is: %c\n" (localtime (current-time)))
- client))
- (("echo" rest ...)
- (format client "~a\n" (string-join rest " ")))
- ;; default
- (_ (display "Sorry, I don't know that command.\n" client))))
-#+END_SRC
-
-Okay, we have a client, so we handle it!
-And once again... we see this goes off on a loop of its own!
-(Also once again, we have to do the =set-port-nonblocking!= song and
-dance.)
-This loop also automatically suspends when it would otherwise block...
-as long as read-line has information to process, it'll keep going, but
-if it would have blocked waiting for input, then it would suspend the
-agenda.[fn:setvbuf]
-
-The actual method called whenever we have a "line" of input is pretty
-straightforward... in fact it looks an awful lot like the IRC bot
-handle-line procedure we used earlier.
-No surprises there![fn:why-send-a-message-to-handle-line]
-
-Now let's run it:
-
-#+BEGIN_SRC scheme
- (let* ((hive (make-hive))
- (telcmd (bootstrap-actor hive <telcmd>)))
- (run-hive hive '()))
-#+END_SRC
-
-Open up another terminal... you can connect via telnet:
-
-#+BEGIN_SRC text
-$ telnet localhost 8889
-Trying 127.0.0.1...
-Connected to localhost.
-Escape character is '^]'.
-time
-The time is: Thu Jan 5 03:20:17 2017
-echo this is an echo
-this is an echo
-shmmmmmmorp
-Sorry, I don't know that command.
-#+END_SRC
-
-Horray, it works!
-Type =Ctrl+] Ctrl+d= to exit telnet.
-
-Not so bad!
-There's more that could be optimized, but we'll consider that to be
-advanced topics of discussion.
-
-So that's a pretty solid intro to how 8sync works!
-Now that you've gone through this introduction, we hope you'll have fun
-writing and hooking together your own actors.
-Since actors are so modular, it's easy to have a program that has
-multiple subystems working together.
-You could build a worker queue system that displayed a web interface
-and spat out notifications about when tasks finish to IRC, and making
-all those actors talk to each other should be a piece of cake.
-The sky's the limit!
-
-Happy hacking!
-
-[fn:setvbuf]
- If there's a lot of data coming in and you don't want your I/O loop
- to become too "greedy", take a look at =setvbuf=.
-
-[fn:queued-handler]
- This is customizable: an actor can be set up to queue messages so
- that absolutely no messages are handled until the actor completely
- finishes handling one message.
- Our loop couldn't look quite like this though!
-
-[fn:why-send-a-message-to-handle-line]
- Well, there may be one surprise to a careful observer.
- Why are we sending a message to ourselves?
- Couldn't we have just dropped the argument of "message" to
- telcmd-handle-line and just called it like any other procedure?
- Indeed, we /could/ do that, but sending a message to ourself has
- an added advantage: if we accidentally "break" the
- telcmd-handle-line procedure in some way (say we add a fun new
- command we're playing with it), raising an exception won't break
- and disconnect the client's main loop, it'll just break the
- message handler for that one line, and our telcmd will happily
- chug along accepting another command from the user while we try
- to figure out what happened to the last one.
-
-** An intermission on live hacking
-
-This section is optional, but highly recommended.
-It requires that you're a user of GNU Emacs.
-If you aren't, don't worry... you can forge ahead and come back in case
-you ever do become an Emacs user.
-(If you're more familiar with Vi/Vim style editing, I hear good things
-about Spacemacs...)
-
-Remember all the way back when we were working on the IRC bot?
-So you may have noticed while updating that section that the
-start/stop cycle of hacking isn't really ideal.
-You might either edit a file in your editor, then run it, or
-type the whole program into the REPL, but then you'll have to spend
-extra time copying it to a file.
-Wouldn't it be nice if it were possible to both write code in a
-file and try it as you go?
-And wouldn't it be even better if you could live edit a program
-while it's running?
-
-Luckily, there's a great Emacs mode called Geiser which makes
-editing and hacking and experimenting all happen in harmony.
-And even better, 8sync is optimized for this experience.
-8sync provides easy drop-in "cooperative REPL" support, and
-most code can be simply redefined on the fly in 8sync through Geiser
-and actors will immediately update their behavior, so you can test
-and tweak things as you go.
-
-Okay, enough talking. Let's add it!
-Redefine run-bot like so:
-
-#+BEGIN_SRC scheme
- (define* (run-bot #:key (username "examplebot")
- (server "irc.freenode.net")
- (channels '("##botchat"))
- (repl-path "/tmp/8sync-repl"))
- (define hive (make-hive))
- (define irc-bot
- (bootstrap-actor hive <my-irc-bot>
- #:username username
- #:server server
- #:channels channels))
- (define repl-manager
- (bootstrap-actor hive <repl-manager>
- #:path repl-path))
-
- (run-hive hive '()))
-#+END_SRC
-
-If we put a call to run-bot at the bottom of our file we can call it,
-and the repl-manager will start something we can connect to automatically.
-Horray!
-Now when we run this it'll start up a REPL with a unix domain socket at
-the repl-path.
-We can connect to it in emacs like so:
-
-: M-x geiser-connect-local <RET> guile <RET> /tmp/8sync-repl <RET>
-
-Okay, so what does this get us?
-Well, we can now live edit our program.
-Let's change how our bot behaves a bit.
-Let's change handle-line and tweak how the bot responds to a botsnack.
-Change this part:
-
-#+BEGIN_SRC scheme
- ;; From this:
- ("botsnack"
- (respond "Yippie! *does a dance!*"))
-
- ;; To this:
- ("botsnack"
- (respond "Yippie! *catches botsnack in midair!*"))
-#+END_SRC
-
-Okay, now let's evaluate the change of the definition.
-You can hit "C-M-x" anywhere in the definition to re-evaluate.
-(You can also position your cursor at the end of the definition and press
-"C-x C-e", but I've come to like "C-M-x" better because I can evaluate as soon
-as I'm done writing.)
-Now, on IRC, ask your bot for a botsnack.
-The bot should give the new message... with no need to stop and start the
-program!
-
-Let's fix a bug live.
-Our current program works great if you talk to your bot in the same
-IRC channel, but what if you try to talk to them over private message?
-
-#+BEGIN_SRC text
-IRC> /query examplebot
-<foo-user> examplebot: hi!
-#+END_SRC
-
-Hm, we aren't seeing any response on IRC!
-Huh? What's going on?
-It's time to do some debugging.
-There are plenty of debugging tools in Guile, but sometimes the simplest
-is the nicest, and the simplest debugging route around is good old
-fashioned print debugging.
-
-It turns out Guile has an under-advertised feature which makes print
-debugging really easy called "pk", pronounced "peek".
-What pk accepts a list of arguments, prints out the whole thing,
-but returns the last argument.
-This makes wrapping bits of our code pretty easy to see what's
-going on.
-So let's peek into our program with pk.
-Edit the respond section to see what channel it's really sending
-things to:
-
-#+BEGIN_SRC scheme
- (define-method (handle-line (irc-bot <my-irc-bot>) message
- speaker channel line emote?)
- ;; [... snip ...]
- (define (respond respond-line)
- (<- (actor-id irc-bot) 'send-line (pk 'channel channel)
- respond-line))
- ;; [... snip ...]
- )
-#+END_SRC
-
-Re-evaluate.
-Now let's ping our bot in both the channel and over PM.
-
-#+BEGIN_SRC text
-;;; (channel "##botchat")
-
-;;; (channel "sinkbot")
-#+END_SRC
-
-Oh okay, this makes sense.
-When we're talking in a normal multi-user channel, the channel we see
-the message coming from is the same one we send to.
-But over PM, the channel is a username, and in this case the username
-we're sending our line of text to is ourselves.
-That isn't what we want.
-Let's edit our code so that if we see that the channel we're sending
-to looks like our own username that we respond back to the sender.
-(We can remove the pk now that we know what's going on.)
-
-#+BEGIN_SRC scheme
- (define-method (handle-line (irc-bot <my-irc-bot>) message
- speaker channel line emote?)
- ;; [... snip ...]
- (define (respond respond-line)
- (<- (actor-id irc-bot) 'send-line
- (if (looks-like-me? channel)
- speaker ; PM session
- channel) ; normal IRC channel
- respond-line))
- ;; [... snip ...]
- )
-#+END_SRC
-
-Re-evaluate and test.
-
-#+BEGIN_SRC text
-IRC> /query examplebot
-<foo-user> examplebot: hi!
-<examplebot> Oh hi foo-user!
-#+END_SRC
-
-Horray!
-
-
-* API reference
-
-* Systems reference
-** IRC
-** Web / HTTP
-** COMMENT Websockets
-
-* Addendum
-** Recommended .emacs additions
-
-In order for =mbody-receive= to indent properly, put this in your
-.emacs:
-
-#+BEGIN_SRC emacs-lisp
-(put 'mbody-receive 'scheme-indent-function 2)
-#+END_SRC
-
-** 8sync and Fibers
-
-One other major library for asynchronous communication in Guile-land
-is [[https://github.com/wingo/fibers/][Fibers]].
-There's a lot of overlap:
-
- - Both use Guile's suspendable-ports facility
- - Both communicate between asynchronous processes using message passing;
- you don't have to squint hard to see the relationship between Fibers'
- channels and 8sync's actor inboxes.
-
-However, there are clearly differences too.
-There's a one to one relationship between 8sync actors and an actor inbox,
-whereas each Fibers fiber may read from multiple channels, for example.
-
-Luckily, it turns out there's a clear relationship, based on real,
-actual theory!
-8sync is based on the [[https://en.wikipedia.org/wiki/Actor_model][actor model]] whereas fibers follows
-[[http://usingcsp.com/][Communicating Sequential Processes (CSP)]], which is a form of
-[[https://en.wikipedia.org/wiki/Process_calculus][process calculi]].
-And it turns out, the
-[[https://en.wikipedia.org/wiki/Actor_model_and_process_calculi][relationship between the actor model and process calculi]] is well documented,
-and even more precisely, the
-[[https://en.wikipedia.org/wiki/Communicating_sequential_processes#Comparison_with_the_Actor_Model][relationship between CSP and the actor model]] is well understood too.
-
-So, 8sync and Fibers do take somewhat different approaches, but both
-have a solid theoretical backing... and their theories are well
-understood in terms of each other.
-Good news for theory nerds!
-
-(Since the actors and CSP are [[https://en.wikipedia.org/wiki/Dual_%28mathematics%29][dual]], maybe eventually 8sync will be
-implemented on top of Fibers... that remains to be seen!)
-
A copy of the license is also available from the Free Software
Foundation Web site at @url{http://www.gnu.org/licenses/fdl.html}.
-Altenately, this document is also available under the Lesser General
+Alternately, this document is also available under the Lesser General
Public License, version 3 or later, as published by the Free Software
Foundation.
A copy of the license is also available from the Free Software
Foundation Web site at @url{http://www.gnu.org/licenses/lgpl.html}.
-
@end quotation
@titlepage
@title 8sync
-@subtitle Using 8sync, an asynchronous event loop for Guile
+@subtitle 8sync, asynchronous actors for Guile
@author Christopher Allan Webber
@page
@vskip 0pt plus 1filll
@contents
@ifnottex
-@node Top
+@node Top, Preface, (dir), (dir)
@top 8sync
@insertcopying
@c Insert new nodes with `C-c C-c n'.
@menu
-* Introduction::
-* Acknowledgements::
-* 8sync's license and general comments on copyleft::
-* Installation::
-* Getting started::
-* API Reference::
-* Contributing::
+* Preface::
+* Tutorial::
+* API reference::
+* Systems reference::
+* Addendum::
* Copying This Manual::
* Index::
@end menu
\f
-@node Introduction
-@chapter Introduction
-
-8sync's goal is to make asynchronous programming easy.
-If you've worked with most other asynchronous programming environments,
-you know that it generally isn't.
-Usually asynchronous programming involves entering some sort of
-`callback hell''.
-Some nicer environments like Asyncio for Python provide generator-based
-coroutines, but even these require a lot of work to carefully line up.
-
-Coding in 8sync, on the other hand, looks almost entirely like coding
-anywhere else.
-This is because 8sync makes great use of a cool feature in Guile called
-``delimited continuations'' to power natural-feeling coroutines.
-Because of this, you can invoke your asynchronous code with a small wrapper
-around it, and that code will pop off to complete whatever other task it
-needs to do, and resume your function when it's ready passing back the
-appropriate value.
-(No need to manually chain the coroutines together, and no callback hell at
-all!)
-
-Now that's pretty cool!
+@node Preface
+@chapter Preface
+
+Welcome to 8sync's documentation!
+8sync is an asynchronous programming environment for GNU Guile.
+(Get it? 8sync? Async??? Quiet your groans, it's a great name!)
+
+8sync has some nice properties:
+
+@itemize
+@item
+8sync uses the actor model as its fundamental concurrency
+synchronization mechanism.
+Since the actor model is a "shared nothing" asynchronous
+environment, you don't need to worry about deadlocks or other
+tricky problems common to other asynchronous models.
+Actors are modular units of code and state which communicate
+by sending messages to each other.
+@item
+If you've done enough asynchronous programming, you're probably
+familiar with the dreaded term "callback hell".
+Getting around callback hell usually involves a tradeoff of other,
+still rather difficult to wrap your brain around programming
+patterns.
+8sync uses some clever tricks involving "delimited continuations"
+under the hood to make the code you write look familiar and
+straightforward.
+When you need to send a request to another actor and get some
+information back from it without blocking, there's no need
+to write a separate procedure@dots{} 8sync's scheduler will suspend
+your procedure and wake it back up when a response is ready.
+@item
+Even nonblocking I/O code is straightforward to write.
+Thanks to the "suspendable ports" code introduced in Guile 2.2,
+writing asynchronous, nonblocking networked code looks mostly
+like writing the same synchronous code.
+8sync's scheduler handles suspending and resuming networked
+code that would otherwise block.
+@item
+8sync aims to be "batteries included".
+Useful subsystems for IRC bots, HTTP servers, and so on are
+included out of the box.
+@item
+8sync prioritizes live hacking.
+If using an editor like Emacs with a nice mode like Geiser,
+an 8sync-using developer can change and fine-tune the behavior
+of code @emph{while it runs}.
+This makes both debugging and development much more natural,
+allowing the right designs to evolve under your fingertips.
+A productive hacker is a happy hacker, after all!
+@end itemize
+
+In the future, 8sync will also provide the ability to spawn and
+communicate with actors on different threads, processes, and machines,
+with most code running the same as if actors were running in the same
+execution environment.
+
+But as a caution, 8sync is still very young.
+The API is stabilizing, but not yet stable, and it is not yet well
+"battle-tested".
+Hacker beware!
+But, consider this as much an opportunity as a warning.
+8sync is in a state where there is much room for feedback and
+contributions.
+Your help wanted!
+
+And now, into the wild, beautiful frontier.
+Onward!
+
+@node Tutorial
+@chapter Tutorial
+
+@menu
+* A silly little IRC bot::
+* Writing our own actors::
+* Writing our own network-enabled actor::
+* An intermission on live hacking::
+@end menu
\f
-@node Acknowledgements
-@chapter Acknowledgements
+@node A silly little IRC bot
+@section A silly little IRC bot
-8sync has a number of inspirations:
+IRC! Internet Relay Chat!
+The classic chat protocol of the Internet.
+And it turns out, one of the best places to learn about networked
+programming.@footnote{In the 1990s I remember stumbling into some funky IRC chat rooms and
+being astounded that people there had what they called "bots" hanging
+around.
+From then until now, I've always enjoyed encountering bots whose range
+of functionality has spanned from saying absurd things, to taking
+messages when their "owners" were offline, to reporting the weather,
+to logging meetings for participants.
+And it turns out, IRC bots are a great way to cut your teeth on
+networked programming; since IRC is a fairly simple line-delineated
+protocol, it's a great way to learn to interact with sockets.
+(My first IRC bot helped my team pick a place to go to lunch, previously
+a source of significant dispute!)
+At the time of writing, venture capital awash startups are trying to
+turn chatbots into "big business"@dots{} a strange (and perhaps absurd)
+thing given chat bots being a fairly mundane novelty amongst hackers
+and teenagers everywhere a few decades ago.}
+We ourselves are going to explore chat bots as a basis for getting our
+feet wet in 8sync.
-@itemize @bullet
-@item
-@uref{https://docs.python.org/3.5/library/asyncio.html, asyncio}
-for Python provides a nice asynchronous programming environment, and
-makes great use of generator-style coroutines.
-It's a bit more difficult to work with than 8sync (or so thinks the author)
-because you have to ``line up'' the coroutines.
+First of all, we're going to need to import some modules. Put this at
+the top of your file:
-@item
-@uref{http://dthompson.us/pages/software/sly.html, Sly}
-by David Thompson is an awesome functional reactive game programming
-library for Guile.
-If you want to write graphical games, Sly is almost certainly a better choice
-than 8sync.
-Thanks to David for being very patient in explaining tough concepts;
-experience on hacking Sly greatly informed 8sync's development.
-(Check out Sly, it rocks!)
+@example
+(use-modules (8sync) ; 8sync's agenda and actors
+ (8sync systems irc) ; the irc bot subsystem
+ (oop goops) ; 8sync's actors use GOOPS
+ (ice-9 format) ; basic string formatting
+ (ice-9 match)) ; pattern matching
+@end example
-@item
-Reading @uref{https://mitpress.mit.edu/sicp/, SICP}, particularly
-@uref{https://mitpress.mit.edu/sicp/full-text/book/book-Z-H-19.html#%_chap_3,
- Chapter 3's writings on concurrent systems},
-greatly informed 8sync's design.
+Now we need to add our bot. Initially, it won't do much.
-@item
-Finally, @uref{https://docs.python.org/3.5/library/asyncio.html, XUDD}
-was an earlier ``research project'' that preceeded 8sync.
-It attempted to bring an actor model system to Python.
-However, the author eventually grew frustrated with some of Python's
-limitations, fell in love with Guile, and well... now we have 8sync, which
-is much more general anyway.
+@example
+(define-class <my-irc-bot> (<irc-bot>))
-@end itemize
+(define-method (handle-line (irc-bot <my-irc-bot>) message
+ speaker channel line emote?)
+ (if emote?
+ (format #t "~a emoted ~s in channel ~a\n"
+ speaker line channel)
+ (format #t "~a said ~s in channel ~a\n"
+ speaker line channel)))
+@end example
-The motivation to build 8sync came out of
-@uref{https://lists.gnu.org/archive/html/guile-devel/2015-10/msg00015.html,
- a conversation}
-at the FSF 30th party between Mark Weaver, David Thompson, Andrew
-Engelbrecht, and Christopher Allan Webber over how to build
-an asynchronous event loop for Guile and just what would be needed.
+We've just defined our own IRC bot!
+This is an 8sync actor.
+(8sync uses GOOPS to define actors.)
+We extended the handle-line generic method, so this is the code that
+will be called whenever the IRC bot "hears" anything.
+This method is itself an action handler, hence the second argument
+for @verb{~message~}, which we can ignore for now.
+Pleasantly, the message's argument body is passed in as the rest of
+the arguments.
-A little over a month after that, hacking on 8sync began!
+For now the code is pretty basic: it just outputs whatever it "hears"
+from a user in a channel to the current output port.
+Pretty boring!
+But it should help us make sure we have things working when we kick
+things off.
+
+Speaking of, even though we've defined our actor, it's not running
+yet. Time to fix that!
+
+@example
+(define* (run-bot #:key (username "examplebot")
+ (server "irc.freenode.net")
+ (channels '("##botchat")))
+ (define hive (make-hive))
+ (define irc-bot
+ (bootstrap-actor hive <my-irc-bot>
+ #:username username
+ #:server server
+ #:channels channels))
+ (run-hive hive '()))
+@end example
+
+Actors are connected to something called a "hive", which is a
+special kind of actor that runs and manages all the other actors.
+Actors can spawn other actors, but before we start the hive we use
+this special @verb{~bootstrap-actor~} method.
+It takes the hive as its first argument, the actor class as the second
+argument, and the rest are initialization arguments to the
+actor.
+@verb{~bootstrap-actor~} passes back not the actor itself (we don't
+get access to that usually) but the @strong{id} of the actor.
+(More on this later.)
+Finally we run the hive with run-hive and pass it a list of
+"bootstrapped" messages.
+Normally actors send messages to each other (and sometimes themselves),
+but we need to send a message or messages to start things or else
+nothing is going to happen.
+
+We can run it like:
+
+@example
+(run-bot #:username "some-bot-name") ; be creative!
+@end example
+
+Assuming all the tubes on the internet are properly connected, you
+should be able to join the "##botchat" channel on irc.freenode.net and
+see your bot join as well.
+Now, as you probably guessed, you can't really @emph{do} much yet.
+If you talk to the bot, it'll send messages to the terminal informing
+you as such, but it's hardly a chat bot if it's not chatting yet.
+
+So let's do the most boring (and annoying) thing possible.
+Let's get it to echo whatever we say back to us.
+Change handle-line to this:
+
+@example
+(define-method (handle-line (irc-bot <my-irc-bot>) message
+ speaker channel line emote?)
+ (<- (actor-id irc-bot) 'send-line channel
+ (format #f "Bawwwwk! ~a says: ~a" speaker line)))
+@end example
+
+This will do exactly what it looks like: repeat back whatever anyone
+says like an obnoxious parrot.
+Give it a try, but don't keep it running for too long@dots{} this
+bot is so annoying it's likely to get banned from whatever channel
+you put it in.
+
+This method handler does have the advantage of being simple though.
+It introduces a new concept simply@dots{} sending a message!
+Whenever you see "<-", you can think of that as saying "send this
+message".
+The arguments to "<-" are as follows: the actor sending the message,
+the id of the actor the message is being sent to, the "action" we
+want to invoke (a symbol), and the rest are arguments to the
+"action handler" which is in this case send-line (with itself takes
+two arguments: the channel our bot should send a message to, and
+the line we want it to spit out to the channel).@footnote{8sync's name for sending a message, "<-", comes from older,
+early lisp object oriented systems which were, as it turned out,
+inspired by the actor model!
+Eventually message passing was dropped in favor of something called
+"generic functions" or "generic methods"
+(you may observe we made use of such a thing in extending
+handle-line).
+Many lispers believe that there is no need for message passing
+with generic methods and some advanced functional techniques,
+but in a concurrent environment message passing becomes useful
+again, especially when the communicating objects / actors are not
+in the same address space.}
+
+Normally in the actor model, we don't have direct references to
+an actor, only an identifier.
+This is for two reasons: to quasi-enforce the "shared nothing"
+environment (actors absolutely control their own resources, and
+"all you can do is send a message" to request that they modify
+them) and because@dots{} well, you don't even know where that actor is!
+Actors can be anything, and anywhere.
+It's possible in 8sync to have an actor on a remote hive, which means
+the actor could be on a remote process or even remote machine, and
+in most cases message passing will look exactly the same.
+(There are some exceptions; it's possible for two actors on the same
+hive to "hand off" some special types of data that can't be serialized
+across processes or the network, eg a socket or a closure, perhaps even
+one with mutable state.
+This must be done with care, and the actors should be careful both
+to ensure that they are both local and that the actor handing things
+off no longer accesses that value to preserve the actor model.
+But this is an advanced topic, and we are getting ahead of ourselves.)
+We have to supply the id of the receiving actor, and usually we'd have
+only the identifier.
+But since in this case, since the actor we're sending this to is
+ourselves, we have to pass in our identifier, since the Hive won't
+deliver to anything other than an address.
+
+Astute readers may observe, since this is a case where we are just
+referencing our own object, couldn't we just call "sending a line"
+as a method of our own object without all the message passing?
+Indeed, we do have such a method, so we @emph{could} rewrite handle-line
+like so:
+
+@example
+(define-method (handle-line (irc-bot <my-irc-bot>) message
+ speaker channel line emote?)
+ (irc-bot-send-line irc-bot channel
+ (format #f "Bawwwwk! ~a says: ~a" speaker line)))
+@end example
+
+@dots{} but we want to get you comfortable and familiar with message
+passing, and we'll be making use of this same message passing shortly
+so that @emph{other} actors may participate in communicating with IRC
+through our IRC bot.
+
+Anyway, our current message handler is simply too annoying.
+What we would really like to do is have our bot respond to individual
+"commands" like this:
+
+@example
+<foo-user> examplebot: hi!
+<examplebot> Oh hi foo-user!
+<foo-user> examplebot: botsnack
+<examplebot> Yippie! *does a dance!*
+<foo-user> examplebot: echo I'm a very silly bot
+<examplebot> I'm a very silly bot
+@end example
+
+Whee, that looks like fun!
+To implement it, we're going to pull out Guile's pattern matcher.
+
+@example
+(define-method (handle-line (irc-bot <my-irc-bot>) message
+ speaker channel line emote?)
+ (define my-name (irc-bot-username irc-bot))
+ (define (looks-like-me? str)
+ (or (equal? str my-name)
+ (equal? str (string-concatenate (list my-name ":")))))
+ (match (string-split line #\space)
+ (((? looks-like-me? _) action action-args ...)
+ (match action
+ ;; The classic botsnack!
+ ("botsnack"
+ (<- (actor-id irc-bot) 'send-line channel
+ "Yippie! *does a dance!*"))
+ ;; Return greeting
+ ((or "hello" "hello!" "hello." "greetings" "greetings." "greetings!"
+ "hei" "hei." "hei!" "hi" "hi!")
+ (<- (actor-id irc-bot) 'send-line channel
+ (format #f "Oh hi ~a!" speaker)))
+ ("echo"
+ (<- (actor-id irc-bot) 'send-line channel
+ (string-join action-args " ")))
+
+ ;; ---> Add yours here <---
+
+ ;; Default
+ (_
+ (<- (actor-id irc-bot) 'send-line channel
+ "*stupid puppy look*"))))))
+@end example
+
+Parsing the pattern matcher syntax is left as an exercise for the
+reader.
+
+If you're getting the sense that we could make this a bit less wordy,
+you're right:
+
+@example
+(define-method (handle-line (irc-bot <my-irc-bot>) message
+ speaker channel line emote?)
+ (define my-name (irc-bot-username irc-bot))
+ (define (looks-like-me? str)
+ (or (equal? str my-name)
+ (equal? str (string-concatenate (list my-name ":")))))
+ (define (respond respond-line)
+ (<- (actor-id irc-bot) 'send-line channel
+ respond-line))
+ (match (string-split line #\space)
+ (((? looks-like-me? _) action action-args ...)
+ (match action
+ ;; The classic botsnack!
+ ("botsnack"
+ (respond "Yippie! *does a dance!*"))
+ ;; Return greeting
+ ((or "hello" "hello!" "hello." "greetings" "greetings." "greetings!"
+ "hei" "hei." "hei!" "hi" "hi." "hi!")
+ (respond (format #f "Oh hi ~a!" speaker)))
+ ("echo"
+ (respond (string-join action-args " ")))
+
+ ;; ---> Add yours here <---
+
+ ;; Default
+ (_
+ (respond "*stupid puppy look*"))))))
+@end example
+
+Okay, that looks pretty good!
+Now we have enough information to build an IRC bot that can do a lot
+of things.
+Take some time to experiment with extending the bot a bit before
+moving on to the next section!
+What cool commands can you add?
+
+\f
+@node Writing our own actors
+@section Writing our own actors
+
+Let's write the most basic, boring actor possible.
+How about an actor that start sleeping, and keeps sleeping?
+
+@example
+(use-modules (oop goops)
+ (8sync))
+
+(define-class <sleeper> (<actor>)
+ (actions #:allocation #:each-subclass
+ #:init-value (build-actions
+ (*init* sleeper-loop))))
+
+(define (sleeper-loop actor message)
+ (while (actor-alive? actor)
+ (display "Zzzzzzzz....\n")
+ ;; Sleep for one second
+ (8sleep (sleeper-sleep-secs actor))))
+
+(let* ((hive (make-hive))
+ (sleeper (bootstrap-actor hive <sleeper>)))
+ (run-hive hive '()))
+@end example
+
+We see some particular things in this example.
+One thing is that our @verb{~<sleeper>~} actor has an actions slot.
+This is used to look up what the "action handler" for a message is.
+We have to set the #:allocation to either @verb{~#:each-subclass~} or
+@verb{~#:class~}.@footnote{#:class should be fine, except there is @uref{https://debbugs.gnu.org/cgi/bugreport.cgi?bug=25211,a bug in Guile} which keeps
+us from using it for now.}
+
+The only action handler we've added is for @verb{~*init*~}, which is called
+implicitly when the actor first starts up.
+(This will be true whether we bootstrap the actor before the hive
+starts or create it during the hive's execution.)
+
+In our sleeper-loop we also see a call to "8sleep".
+"8sleep" is like Guile's "sleep" method, except it is non-blocking
+and will always yield to the scheduler.
+
+Our while loop also checks "actor-alive?" to see whether or not
+it is still registered.
+In general, if you keep a loop in your actor that regularly yields
+to the scheduler, you should check this.@footnote{Or rather, for now you should call @verb{~actor-alive?~} if your code
+is looping like this.
+In the future, after an actor dies, its coroutines will
+automatically be "canceled".}
+(An alternate way to handle it would be to not use a while loop at all
+but simply send a message to ourselves with "<-" to call the
+sleeper-loop handler again.
+If the actor was dead, the message simply would not be delivered and
+thus the loop would stop.)
+
+It turns out we could have written the class for the actor much more
+simply:
+
+@example
+;; You could do this instead of the define-class above.
+(define-actor <sleeper> (<actor>)
+ ((*init* sleeper-loop)))
+@end example
+
+This is sugar, and expands into exactly the same thing as the
+define-class above.
+The third argument is an argument list, the same as what's passed
+into build-actions.
+Everything after that is a slot.
+So for example, if we had added an optional slot to specify
+how many seconds to sleep, we could have done it like so:
+
+@example
+(define-actor <sleeper> (<actor>)
+ ((*init* sleeper-loop))
+ (sleep-secs #:init-value 1
+ #:getter sleeper-sleep-secs))
+@end example
+
+This actor is pretty lazy though.
+Time to get back to work!
+Let's build a worker / manager type system.
+
+@example
+(use-modules (8sync)
+ (oop goops))
+
+(define-actor <manager> (<actor>)
+ ((assign-task manager-assign-task))
+ (direct-report #:init-keyword #:direct-report
+ #:getter manager-direct-report))
+
+(define (manager-assign-task manager message difficulty)
+ "Delegate a task to our direct report"
+ (display "manager> Work on this task for me!\n")
+ (<- (manager-direct-report manager)
+ 'work-on-this difficulty))
+@end example
+
+This manager keeps track of a direct report and tells them to start
+working on a task@dots{} simple delegation.
+Nothing here is really new, but note that our friend "<-" (which means
+"send message") is back.
+There's one difference this time@dots{} the first time we saw "<-" was in
+the handle-line procedure of the irc-bot, and in that case we explicitly
+pulled the actor-id after the actor we were sending the message to
+(ourselves), which we aren't doing here.
+But that was an unusual case, because the actor was ourself.
+In this case, and in general, actors don't have direct references to
+other actors; instead, all they have is access to identifiers which
+reference other actors.
+
+@example
+(define-actor <worker> (<actor>)
+ ((work-on-this worker-work-on-this))
+ (task-left #:init-keyword #:task-left
+ #:accessor worker-task-left))
+
+(define (worker-work-on-this worker message difficulty)
+ "Work on one task until done."
+ (set! (worker-task-left worker) difficulty)
+ (display "worker> Whatever you say, boss!\n")
+ (while (and (actor-alive? worker)
+ (> (worker-task-left worker) 0))
+ (display "worker> *huff puff*\n")
+ (set! (worker-task-left worker)
+ (- (worker-task-left worker) 1))
+ (8sleep (/ 1 3))))
+@end example
+
+The worker also contains familiar code, but we now see that we can
+call 8sleep with non-integer real numbers.
+
+Looks like there's nothing left to do but run it.
+
+@example
+(let* ((hive (make-hive))
+ (worker (bootstrap-actor hive <worker>))
+ (manager (bootstrap-actor hive <manager>
+ #:direct-report worker)))
+ (run-hive hive (list (bootstrap-message hive manager 'assign-task 5))))
+@end example
+
+Unlike the @verb{~<sleeper>~}, our @verb{~<manager>~} doesn't have an implicit
+@verb{~*init*~} method, so we've bootstrapped the calling @verb{~assign-task~} action.
+
+@example
+manager> Work on this task for me!
+worker> Whatever you say, boss!
+worker> *huff puff*
+worker> *huff puff*
+worker> *huff puff*
+worker> *huff puff*
+worker> *huff puff*
+@end example
+
+"<-" pays no attention to what happens with the messages it has sent
+off.
+This is useful in many cases@dots{} we can blast off many messages and
+continue along without holding anything back.
+
+But sometimes we want to make sure that something completes before
+we do something else, or we want to send a message and get some sort
+of information back.
+Luckily 8sync comes with an answer to that with "<-wait", which will
+suspend the caller until the callee gives some sort of response, but
+which does not block the rest of the program from running.
+Let's try applying that to our own code by turning our manager
+into a micromanager.
+
+@example
+;;; Update this method
+(define (manager-assign-task manager message difficulty)
+ "Delegate a task to our direct report"
+ (display "manager> Work on this task for me!\n")
+ (<- (manager-direct-report manager)
+ 'work-on-this difficulty)
+
+ ;; Wait a moment, then call the micromanagement loop
+ (8sleep (/ 1 2))
+ (manager-micromanage-loop manager))
+
+;;; And add the following
+;;; (... Note: do not model actual employee management off this)
+(define (manager-micromanage-loop manager)
+ "Pester direct report until they're done with their task."
+ (display "manager> Are you done yet???\n")
+ (let ((worker-is-done
+ (mbody-val (<-wait (manager-direct-report manager)
+ 'done-yet?))))
+ (if worker-is-done
+ (begin (display "manager> Oh! I guess you can go home then.\n")
+ (<- (manager-direct-report manager) 'go-home))
+ (begin (display "manager> Harumph!\n")
+ (8sleep (/ 1 2))
+ (when (actor-alive? manager)
+ (manager-micromanage-loop manager))))))
+@end example
+
+We've appended a micromanagement loop here@dots{} but what's going on?
+"<-wait", as it sounds, waits for a reply, and returns a reply
+message.
+In this case there's a value in the body of the message we want,
+so we pull it out with mbody-val.
+(It's possible for a remote actor to return multiple values, in which
+case we'd want to use mbody-receive, but that's a bit more
+complicated.)
+
+Of course, we need to update our worker accordingly as well.
+
+@example
+;;; Update the worker to add the following new actions:
+(define-actor <worker> (<actor>)
+ ((work-on-this worker-work-on-this)
+ ;; Add these:
+ (done-yet? worker-done-yet?)
+ (go-home worker-go-home))
+ (task-left #:init-keyword #:task-left
+ #:accessor worker-task-left))
+
+;;; New procedures:
+(define (worker-done-yet? worker message)
+ "Reply with whether or not we're done yet."
+ (let ((am-i-done? (= (worker-task-left worker) 0)))
+ (if am-i-done?
+ (display "worker> Yes, I finished up!\n")
+ (display "worker> No... I'm still working on it...\n"))
+ (<-reply message am-i-done?)))
+
+(define (worker-go-home worker message)
+ "It's off of work for us!"
+ (display "worker> Whew! Free at last.\n")
+ (self-destruct worker))
+@end example
+
+(As you've probably guessed, you wouldn't normally call @verb{~display~}
+everywhere as we are in this program@dots{} that's just to make the
+examples more illustrative.)
+
+"<-reply" is what actually returns the information to the actor
+waiting on the reply.
+It takes as an argument the actor sending the message, the message
+it is in reply to, and the rest of the arguments are the "body" of
+the message.
+(If an actor handles a message that is being "waited on" but does not
+explicitly reply to it, an auto-reply with an empty body will be
+triggered so that the waiting actor is not left waiting around.)
+
+The last thing to note is the call to "self-destruct".
+This does what you might expect: it removes the actor from the hive.
+No new messages will be sent to it.
+Ka-poof!
+
+Running it is the same as before:
+
+@example
+(let* ((hive (make-hive))
+ (worker (bootstrap-actor hive <worker>))
+ (manager (bootstrap-actor hive <manager>
+ #:direct-report worker)))
+ (run-hive hive (list (bootstrap-message hive manager 'assign-task 5))))
+@end example
+
+But the output is a bit different:
+
+@example
+manager> Work on this task for me!
+worker> Whatever you say, boss!
+worker> *huff puff*
+worker> *huff puff*
+manager> Are you done yet???
+worker> No... I'm still working on it...
+manager> Harumph!
+worker> *huff puff*
+manager> Are you done yet???
+worker> *huff puff*
+worker> No... I'm still working on it...
+manager> Harumph!
+worker> *huff puff*
+manager> Are you done yet???
+worker> Yes, I finished up!
+manager> Oh! I guess you can go home then.
+worker> Whew! Free at last.
+@end example
\f
+@node Writing our own network-enabled actor
+@section Writing our own network-enabled actor
+So, you want to write a networked actor!
+Well, luckily that's pretty easy, especially with all you know so far.
+
+@example
+(use-modules (oop goops)
+ (8sync)
+ (ice-9 rdelim) ; line delineated i/o
+ (ice-9 match)) ; pattern matching
+
+(define-actor <telcmd> (<actor>)
+ ((*init* telcmd-init)
+ (*cleanup* telcmd-cleanup)
+ (new-client telcmd-new-client)
+ (handle-line telcmd-handle-line))
+ (socket #:accessor telcmd-socket
+ #:init-value #f))
+@end example
+
+Nothing surprising about the actor definition, though we do see that
+it has a slot for a socket.
+Unsurprisingly, that will be set up in the @verb{~*init*~} handler.
+
+@example
+(define (set-port-nonblocking! port)
+ (let ((flags (fcntl port F_GETFL)))
+ (fcntl port F_SETFL (logior O_NONBLOCK flags))))
+
+(define (setup-socket)
+ ;; our socket
+ (define s
+ (socket PF_INET SOCK_STREAM 0))
+ ;; reuse port even if busy
+ (setsockopt s SOL_SOCKET SO_REUSEADDR 1)
+ ;; connect to port 8889 on localhost
+ (bind s AF_INET INADDR_LOOPBACK 8889)
+ ;; make it nonblocking and start listening
+ (set-port-nonblocking! s)
+ (listen s 5)
+ s)
+
+(define (telcmd-init telcmd message)
+ (set! (telcmd-socket telcmd) (setup-socket))
+ (display "Connect like: telnet localhost 8889\n")
+ (while (actor-alive? telcmd)
+ (let ((client-connection (accept (telcmd-socket telcmd))))
+ (<- (actor-id telcmd) 'new-client client-connection))))
+
+(define (telcmd-cleanup telcmd message)
+ (display "Closing socket!\n")
+ (when (telcmd-socket telcmd)
+ (close (telcmd-socket telcmd))))
+@end example
+
+That @verb{~setup-socket~} code looks pretty hard to read!
+But that's pretty standard code for setting up a socket.
+One special thing is done though@dots{} the call to
+@verb{~set-port-nonblocking!~} sets flags on the socket port so that,
+you guessed it, will be a nonblocking port.
+
+This is put to immediate use in the telcmd-init method.
+This code looks suspiciously like it @emph{should} block@dots{} after
+all, it just keeps looping forever.
+But since 8sync is using Guile's suspendable ports code feature,
+so every time this loop hits the @verb{~accept~} call, if that call
+@emph{would have} blocked, instead this whole procedure suspends
+to the scheduler@dots{} automatically!@dots{} allowing other code to run.
+
+So, as soon as we do accept a connection, we send a message to
+ourselves with the @verb{~new-client~} action.
+But wait!
+Aren't actors only supposed to handle one message at a time?
+If the telcmd-init loop just keeps on looping and looping,
+when will the @verb{~new-client~} message ever be handled?
+8sync actors only receive one message at a time, but by default if an
+actor's message handler suspends to the agenda for some reason (such
+as to send a message or on handling I/O), that actor may continue to
+accept other messages, but always in the same thread.@footnote{This is customizable: an actor can be set up to queue messages so
+that absolutely no messages are handled until the actor completely
+finishes handling one message.
+Our loop couldn't look quite like this though!}
+
+We also see that we've established a @verb{~*cleanup*~} handler.
+This is run any time either the actor dies, either through self
+destructing, because the hive completes its work, or because
+a signal was sent to interrupt or terminate our program.
+In our case, we politely close the socket when @verb{~<telcmd>~} dies.
+
+@example
+(define (telcmd-new-client telcmd message client-connection)
+ (define client (car client-connection))
+ (set-port-nonblocking! client)
+ (let loop ()
+ (let ((line (read-line client)))
+ (cond ((eof-object? line)
+ (close client))
+ (else
+ (<- (actor-id telcmd) 'handle-line
+ client (string-trim-right line #\return))
+ (when (actor-alive? telcmd)
+ (loop)))))))
+
+(define (telcmd-handle-line telcmd message client line)
+ (match (string-split line #\space)
+ (("") #f) ; ignore empty lines
+ (("time" _ ...)
+ (display
+ (strftime "The time is: %c\n" (localtime (current-time)))
+ client))
+ (("echo" rest ...)
+ (format client "~a\n" (string-join rest " ")))
+ ;; default
+ (_ (display "Sorry, I don't know that command.\n" client))))
+@end example
+
+Okay, we have a client, so we handle it!
+And once again@dots{} we see this goes off on a loop of its own!
+(Also once again, we have to do the @verb{~set-port-nonblocking!~} song and
+dance.)
+This loop also automatically suspends when it would otherwise block@dots{}
+as long as read-line has information to process, it'll keep going, but
+if it would have blocked waiting for input, then it would suspend the
+agenda.@footnote{If there's a lot of data coming in and you don't want your I/O loop
+to become too "greedy", take a look at @verb{~setvbuf~}.}
+
+The actual method called whenever we have a "line" of input is pretty
+straightforward@dots{} in fact it looks an awful lot like the IRC bot
+handle-line procedure we used earlier.
+No surprises there!@footnote{Well, there may be one surprise to a careful observer.
+Why are we sending a message to ourselves?
+Couldn't we have just dropped the argument of "message" to
+telcmd-handle-line and just called it like any other procedure?
+Indeed, we @emph{could} do that, but sending a message to ourself has
+an added advantage: if we accidentally "break" the
+telcmd-handle-line procedure in some way (say we add a fun new
+command we're playing with it), raising an exception won't break
+and disconnect the client's main loop, it'll just break the
+message handler for that one line, and our telcmd will happily
+chug along accepting another command from the user while we try
+to figure out what happened to the last one.}
+
+Now let's run it:
+
+@example
+(let* ((hive (make-hive))
+ (telcmd (bootstrap-actor hive <telcmd>)))
+ (run-hive hive '()))
+@end example
+
+Open up another terminal@dots{} you can connect via telnet:
+
+@example
+$ telnet localhost 8889
+Trying 127.0.0.1...
+Connected to localhost.
+Escape character is '^]'.
+time
+The time is: Thu Jan 5 03:20:17 2017
+echo this is an echo
+this is an echo
+shmmmmmmorp
+Sorry, I don't know that command.
+@end example
+
+Horray, it works!
+Type @verb{~Ctrl+] Ctrl+d~} to exit telnet.
+
+Not so bad!
+There's more that could be optimized, but we'll consider that to be
+advanced topics of discussion.
+
+So that's a pretty solid intro to how 8sync works!
+Now that you've gone through this introduction, we hope you'll have fun
+writing and hooking together your own actors.
+Since actors are so modular, it's easy to have a program that has
+multiple subystems working together.
+You could build a worker queue system that displayed a web interface
+and spat out notifications about when tasks finish to IRC, and making
+all those actors talk to each other should be a piece of cake.
+The sky's the limit!
+
+Happy hacking!
+
+\f
+@node An intermission on live hacking
+@section An intermission on live hacking
+
+This section is optional, but highly recommended.
+It requires that you're a user of GNU Emacs.
+If you aren't, don't worry@dots{} you can forge ahead and come back in case
+you ever do become an Emacs user.
+(If you're more familiar with Vi/Vim style editing, I hear good things
+about Spacemacs@dots{})
+
+Remember all the way back when we were working on the IRC bot?
+So you may have noticed while updating that section that the
+start/stop cycle of hacking isn't really ideal.
+You might either edit a file in your editor, then run it, or
+type the whole program into the REPL, but then you'll have to spend
+extra time copying it to a file.
+Wouldn't it be nice if it were possible to both write code in a
+file and try it as you go?
+And wouldn't it be even better if you could live edit a program
+while it's running?
+
+Luckily, there's a great Emacs mode called Geiser which makes
+editing and hacking and experimenting all happen in harmony.
+And even better, 8sync is optimized for this experience.
+8sync provides easy drop-in "cooperative REPL" support, and
+most code can be simply redefined on the fly in 8sync through Geiser
+and actors will immediately update their behavior, so you can test
+and tweak things as you go.
+
+Okay, enough talking. Let's add it!
+Redefine run-bot like so:
+
+@example
+(define* (run-bot #:key (username "examplebot")
+ (server "irc.freenode.net")
+ (channels '("##botchat"))
+ (repl-path "/tmp/8sync-repl"))
+ (define hive (make-hive))
+ (define irc-bot
+ (bootstrap-actor hive <my-irc-bot>
+ #:username username
+ #:server server
+ #:channels channels))
+ (define repl-manager
+ (bootstrap-actor hive <repl-manager>
+ #:path repl-path))
+
+ (run-hive hive '()))
+@end example
+
+If we put a call to run-bot at the bottom of our file we can call it,
+and the repl-manager will start something we can connect to automatically.
+Horray!
+Now when we run this it'll start up a REPL with a unix domain socket at
+the repl-path.
+We can connect to it in emacs like so:
+
+@example
+M-x geiser-connect-local <RET> guile <RET> /tmp/8sync-repl <RET>
+
+@end example
+
+Okay, so what does this get us?
+Well, we can now live edit our program.
+Let's change how our bot behaves a bit.
+Let's change handle-line and tweak how the bot responds to a botsnack.
+Change this part:
+
+@example
+;; From this:
+("botsnack"
+ (respond "Yippie! *does a dance!*"))
+
+;; To this:
+("botsnack"
+ (respond "Yippie! *catches botsnack in midair!*"))
+@end example
+
+Okay, now let's evaluate the change of the definition.
+You can hit "C-M-x" anywhere in the definition to re-evaluate.
+(You can also position your cursor at the end of the definition and press
+"C-x C-e", but I've come to like "C-M-x" better because I can evaluate as soon
+as I'm done writing.)
+Now, on IRC, ask your bot for a botsnack.
+The bot should give the new message@dots{} with no need to stop and start the
+program!
+
+Let's fix a bug live.
+Our current program works great if you talk to your bot in the same
+IRC channel, but what if you try to talk to them over private message?
+
+@example
+IRC> /query examplebot
+<foo-user> examplebot: hi!
+@end example
+
+Hm, we aren't seeing any response on IRC!
+Huh? What's going on?
+It's time to do some debugging.
+There are plenty of debugging tools in Guile, but sometimes the simplest
+is the nicest, and the simplest debugging route around is good old
+fashioned print debugging.
+
+It turns out Guile has an under-advertised feature which makes print
+debugging really easy called "pk", pronounced "peek".
+What pk accepts a list of arguments, prints out the whole thing,
+but returns the last argument.
+This makes wrapping bits of our code pretty easy to see what's
+going on.
+So let's peek into our program with pk.
+Edit the respond section to see what channel it's really sending
+things to:
+
+@example
+(define-method (handle-line (irc-bot <my-irc-bot>) message
+ speaker channel line emote?)
+ ;; [... snip ...]
+ (define (respond respond-line)
+ (<- (actor-id irc-bot) 'send-line (pk 'channel channel)
+ respond-line))
+ ;; [... snip ...]
+ )
+@end example
+
+Re-evaluate.
+Now let's ping our bot in both the channel and over PM.
+
+@example
+;;; (channel "##botchat")
+
+;;; (channel "sinkbot")
+@end example
+
+Oh okay, this makes sense.
+When we're talking in a normal multi-user channel, the channel we see
+the message coming from is the same one we send to.
+But over PM, the channel is a username, and in this case the username
+we're sending our line of text to is ourselves.
+That isn't what we want.
+Let's edit our code so that if we see that the channel we're sending
+to looks like our own username that we respond back to the sender.
+(We can remove the pk now that we know what's going on.)
+
+@example
+(define-method (handle-line (irc-bot <my-irc-bot>) message
+ speaker channel line emote?)
+ ;; [... snip ...]
+ (define (respond respond-line)
+ (<- (actor-id irc-bot) 'send-line
+ (if (looks-like-me? channel)
+ speaker ; PM session
+ channel) ; normal IRC channel
+ respond-line))
+ ;; [... snip ...]
+ )
+@end example
+
+Re-evaluate and test.
+
+@example
+IRC> /query examplebot
+<foo-user> examplebot: hi!
+<examplebot> Oh hi foo-user!
+@end example
+
+Horray!
+
+\f
+@node API reference
+@chapter API reference
+
+\f
+@node Systems reference
+@chapter Systems reference
+
+@menu
+* IRC::
+* Web / HTTP::
+@end menu
+
+\f
+@node IRC
+@section IRC
+
+\f
+@node Web / HTTP
+@section Web / HTTP
+
+\f
+@node Addendum
+@chapter Addendum
+
+@menu
+* Recommended emacs additions::
+* 8sync and Fibers::
+* 8sync's license and general comments on copyleft::
+* Acknowledgements::
+@end menu
+
+\f
+@node Recommended emacs additions
+@section Recommended emacs additions
+
+In order for @verb{~mbody-receive~} to indent properly, put this in your
+.emacs:
+
+@lisp
+(put 'mbody-receive 'scheme-indent-function 2)
+@end lisp
+
+@node 8sync and Fibers
+@section 8sync and Fibers
+
+One other major library for asynchronous communication in Guile-land
+is @uref{https://github.com/wingo/fibers/,Fibers}.
+There's a lot of overlap:
+
+@itemize
+@item
+Both use Guile's suspendable-ports facility
+@item
+Both communicate between asynchronous processes using message passing;
+you don't have to squint hard to see the relationship between Fibers'
+channels and 8sync's actor inboxes.
+@end itemize
+
+However, there are clearly differences too.
+There's a one to one relationship between 8sync actors and an actor inbox,
+whereas each Fibers fiber may read from multiple channels, for example.
+
+Luckily, it turns out there's a clear relationship, based on real,
+actual theory!
+8sync is based on the @uref{https://en.wikipedia.org/wiki/Actor_model,actor model} whereas fibers follows
+@uref{http://usingcsp.com/,Communicating Sequential Processes (CSP)}, which is a form of
+@uref{https://en.wikipedia.org/wiki/Process_calculus,process calculi}.
+And it turns out, the
+@uref{https://en.wikipedia.org/wiki/Actor_model_and_process_calculi,relationship between the actor model and process calculi} is well documented,
+and even more precisely, the
+@uref{https://en.wikipedia.org/wiki/Communicating_sequential_processes#Comparison_with_the_Actor_Model,relationship between CSP and the actor model} is well understood too.
+
+So, 8sync and Fibers do take somewhat different approaches, but both
+have a solid theoretical backing@dots{} and their theories are well
+understood in terms of each other.
+Good news for theory nerds!
+
+(Since the actors and CSP are @uref{https://en.wikipedia.org/wiki/Dual_%28mathematics%29,dual}, maybe eventually 8sync will be
+implemented on top of Fibers@dots{} that remains to be seen!)
+
+\f
@node 8sync's license and general comments on copyleft
-@chapter 8sync's license and general comments on copyleft
+@section 8sync's license and general comments on copyleft
8sync is released under the GNU LGPL (Lesser General Public License),
version 3 or later, as published by the Free Software Foundation.
@uref{https://www.gnu.org/licenses/why-not-lgpl.html,
Why you shouldn't use the Lesser GPL for your next library}.)
-Although 8sync provides some unique features, its main functionality is as
-an asynchronous event loop, and there are many other asynchronous event
-loop systems out there such as Node.js for Javascript and Asyncio for
-Python (there are others as well).
+Although 8sync provides some unique features, its main functionality
+is as an asynchronous programming environment, and there are many
+other asynchronous programming environments out there such as Node.js
+for Javascript and Asyncio for Python (there are others as well).
It is popular in some of these communities to hold anti-copyleft positions,
which is unfortunate, and many community members seem to be adopting
these positions because other developers they look up to are holding
And help us turn the tide towards greater software freedom...
consider a strong copyleft license!''
-\f
+@node Acknowledgements
+@section Acknowledgements
-@node Installation
-@chapter Installation
+8sync has a number of inspirations:
-General GNU configure / make / make install instructions go here!
-:)
+@itemize @bullet
+@item
+@uref{https://docs.python.org/3.5/library/asyncio.html, asyncio}
+for Python provides a nice asynchronous programming environment, and
+makes great use of generator-style coroutines.
+It's a bit more difficult to work with than 8sync (or so thinks the author)
+because you have to ``line up'' the coroutines.
-\f
+@item
+@uref{http://dthompson.us/pages/software/sly.html, Sly}
+by David Thompson is an awesome functional reactive game programming
+library for Guile.
+If you want to write graphical games, Sly is almost certainly a better choice
+than 8sync.
+Thanks to David for being very patient in explaining tough concepts;
+experience on hacking Sly greatly informed 8sync's development.
+(Check out Sly, it rocks!)
-@node Getting started
-@chapter Getting started
+@item
+Reading @uref{https://mitpress.mit.edu/sicp/, SICP}, particularly
+@uref{https://mitpress.mit.edu/sicp/full-text/book/book-Z-H-19.html#%_chap_3,
+ Chapter 3's writings on concurrent systems},
+greatly informed 8sync's design.
-\f
+@item
+Finally, @uref{http://xudd.readthedocs.io/en/latest/, XUDD}
+was an earlier ``research project'' that preceeded 8sync.
+It attempted to bring an actor model system to Python.
+However, the author eventually grew frustrated with some of Python's
+limitations, fell in love with Guile, and well... now we have 8sync.
+Much of 8sync's actor model design came from experiments in developing
+XUDD.
-@node API Reference
-@chapter API Reference
+@end itemize
-\f
+The motivation to build 8sync came out of
+@uref{https://lists.gnu.org/archive/html/guile-devel/2015-10/msg00015.html,
+ a conversation}
+at the FSF 30th party between Mark Weaver, David Thompson, Andrew
+Engelbrecht, and Christopher Allan Webber over how to build
+an asynchronous event loop for Guile and just what would be needed.
-@node Contributing
-@chapter Contributing
+A little over a month after that, hacking on 8sync began!
\f