+** 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))
+ (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
+ (telcmd-handle-line telcmd client
+ (string-trim-right line #\return))
+ (when (actor-alive? telcmd)
+ (loop)))))))
+
+ (define (telcmd-handle-line telcmd 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!
+
+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!
+
+** An intermission: about 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> "irc-bot"
+ #:username username
+ #:server server
+ #:channels channels))
+ (define repl-manager
+ (bootstrap-actor* hive <repl-manager> "repl"
+ #: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>) 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>) 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!
+