1 # Permission is granted to copy, distribute and/or modify this document
2 # under the terms of the GNU Free Documentation License, Version 1.3
3 # or any later version published by the Free Software Foundation;
4 # with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
5 # A copy of the license is included in the section entitled ``GNU
6 # Free Documentation License''.
8 # A copy of the license is also available from the Free Software
9 # Foundation Web site at http://www.gnu.org/licenses/fdl.html
11 # Altenately, this document is also available under the Lesser General
12 # Public License, version 3 or later, as published by the Free Software
15 # A copy of the license is also available from the Free Software
16 # Foundation Web site at http://www.gnu.org/licenses/lgpl.html
20 Welcome to 8sync's documentation!
21 8sync is an asynchronous programming environment for GNU Guile.
22 (Get it? 8sync? Async??? Quiet your groans, it's a great name!)
24 8sync has some nice properties:
26 - 8sync uses the actor model as its fundamental concurrency
27 synchronization mechanism.
28 Since the actor model is a "shared nothing" asynchronous
29 environment, you don't need to worry about deadlocks or other
30 tricky problems common to other asynchronous models.
31 Actors are modular units of code and state which communicate
32 by sending messages to each other.
33 - If you've done enough asynchronous programming, you're probably
34 familiar with the dreaded term "callback hell".
35 Getting around callback hell usually involves a tradeoff of other,
36 still rather difficult to wrap your brain around programming
38 8sync uses some clever tricks involving "delimited continuations"
39 under the hood to make the code you write look familiar and
41 When you need to send a request to another actor and get some
42 information back from it without blocking, there's no need
43 to write a separate procedure... 8sync's scheduler will suspend
44 your procedure and wake it back up when a response is ready.
45 - Even nonblocking I/O code is straightforward to write.
46 Thanks to the "suspendable ports" code introduced in Guile 2.2,
47 writing asynchronous, nonblocking networked code looks mostly
48 like writing the same synchronous code.
49 8sync's scheduler handles suspending and resuming networked
50 code that would otherwise block.
51 - 8sync aims to be "batteries included".
52 Useful subsystems for IRC bots, HTTP servers, and so on are
53 included out of the box.
54 - 8sync prioritizes live hacking.
55 If using an editor like Emacs with a nice mode like Geiser,
56 an 8sync-using developer can change and fine-tune the behavior
57 of code /while it runs/.
58 This makes both debugging and development much more natural,
59 allowing the right designs to evolve under your fingertips.
60 A productive hacker is a happy hacker, after all!
62 In the future, 8sync will also provide the ability to spawn and
63 communicate with actors on different threads, processes, and machines,
64 with most code running the same as if actors were running in the same
65 execution environment.
67 But as a caution, 8sync is still very young.
68 The API is stabilizing, but not yet stable, and it is not yet well
71 But, consider this as much an opportunity as a warning.
72 8sync is in a state where there is much room for feedback and
76 And now, into the wild, beautiful frontier.
81 ** Intro to the tutorial
83 IRC! Internet Relay Chat!
84 The classic chat protocol of the Internet.
85 And it turns out, one of the best places to learn about networked
88 In the 1990s I remember stumbling into some funky IRC chat rooms and
89 being astounded that people there had what they called "bots" hanging
91 From then until now, I've always enjoyed encountering bots whose range
92 of functionality has spanned from saying absurd things, to taking
93 messages when their "owners" were offline, to reporting the weather,
94 to logging meetings for participants.
95 And it turns out, IRC bots are a great way to cut your teeth on
96 networked programming; since IRC is a fairly simple line-delineated
97 protocol, it's a great way to learn to interact with sockets.
98 (My first IRC bot helped my team pick a place to go to lunch, previously
99 a source of significant dispute!)
100 At the time of writing, venture capital awash startups are trying to
101 turn chatbots into "big business"... a strange (and perhaps absurd)
102 thing given chat bots being a fairly mundane novelty amongst hackers
103 and teenagers everywhere in the 1990s.
105 We ourselves are going to explore chat bots as a basis for getting our
107 We'll start from a minimalist example using an irc bot with most of
108 the work done for us, then move on to constructing our own actors as
109 "game pieces" which interface with our bot, then experiment with just
110 how easy it is to add new networked layers by tacking on a high score
111 to our game, and as a finale we'll dive into writing our own little
112 irc bot framework "from scratch" on top of the 8sync actor model.
114 Alright, let's get started.
115 This should be a lot of fun!
117 ** A silly little IRC bot
119 First of all, we're going to need to import some modules. Put this at
120 the top of your file:
123 (use-modules (8sync) ; 8sync's agenda and actors
124 (8sync systems irc) ; the irc bot subsystem
125 (oop goops) ; 8sync's actors use GOOPS
126 (ice-9 format) ; basic string formatting
127 (ice-9 match)) ; pattern matching
130 Now we need to add our bot. Initially, it won't do much.
133 (define-class <my-irc-bot> (<irc-bot>))
135 (define-method (handle-line (irc-bot <my-irc-bot>) speaker channel
138 (format #t "~a emoted ~s in channel ~a\n"
139 speaker line channel)
140 (format #t "~a said ~s in channel ~a\n"
141 speaker line channel)))
144 We've just defined our own IRC bot!
145 This is an 8sync actor.
146 (8sync uses GOOPS to define actors.)
147 We extended the handle-line generic method, so this is the code that
148 will be called whenever the IRC bot "hears" anything.
149 For now the code is pretty basic: it just outputs whatever it "hears"
150 from a user in a channel to the current output port.
152 But it should help us make sure we have things working when we kick
155 Speaking of, even though we've defined our actor, it's not running
156 yet. Time to fix that!
159 (define* (run-bot #:key (username "examplebot")
160 (server "irc.freenode.net")
161 (channels '("##botchat")))
162 (define hive (make-hive))
164 (hive-create-actor* hive <my-irc-bot> "irc-bot"
167 #:channels channels))
168 (run-hive hive (list (bootstrap-message hive irc-bot 'init))))
171 Actors are connected to something called a "hive", which is a
172 special kind of actor that runs all the other actors.
173 Actors can spawn other actors, but before we start the hive we use
174 this special "hive-create-actor*" method.
175 It takes the hive as its first argument, the actor class as the second
176 argument, a decoraive "cookie" as the third argument (this is
177 optional, but it helps with debugging... you can skip it by setting it
178 to #f if you prefer), and the rest are initialization arguments to the
179 actor. hive-create-actor* passes back not the actor itself (we don't
180 get access to that usually) but the *id* of the actor.
181 (More on this later.)
182 Finally we run the hive with run-hive and pass it a list of
183 "bootstrapped" messages.
184 Normally actors send messages to each other (and sometimes themselves),
185 but we need to send a message or messages to start things or else
186 nothing is going to happen.
191 (run-bot #:username "some-bot-username") ; be creative!
194 Assuming all the tubes on the internet are properly connected, you
195 should be able to join the "##botchat" channel on irc.freenode.net and
196 see your bot join as well.
197 Now, as you probably guessed, you can't really /do/ much yet.
198 If you talk to the bot, it'll send messages to the terminal informing
199 you as such, but it's hardly a chat bot if it's not chatting yet.
201 So let's do the most boring (and annoying) thing possible.
202 Let's get it to echo whatever we say back to us.
203 Change handle-line to this:
206 (define-method (handle-line (irc-bot <my-irc-bot>) speaker channel
208 (<- irc-bot (actor-id irc-bot) 'send-line channel
209 (format #f "Bawwwwk! ~a says: ~a" speaker line)))
212 This will do exactly what it looks like: repeat back whatever anyone
213 says like an obnoxious parrot.
214 Give it a try, but don't keep it running for too long... this
215 bot is so annoying it's likely to get banned from whatever channel
218 This method handler does have the advantage of being simple though.
219 It introduces a new concept simply... sending a message!
220 Whenever you see "<-", you can think of that as saying "send this
222 The arguments to "<-" are as follows: the actor sending the message,
223 the id of the actor the message is being sent to, the "action" we
224 want to invoke (a symbol), and the rest are arguments to the
225 "action handler" which is in this case send-line (with itself takes
226 two arguments: the channel our bot should send a message to, and
227 the line we want it to spit out to the channel).
229 (Footnote: 8sync's name for sending a message, "<-", comes from older,
230 early lisp object oriented systems which were, as it turned out,
231 inspired by the actor model!
232 Eventually message passing was dropped in favor of something called
233 "generic functions" or "generic methods"
234 (you may observe we made use of such a thing in extending
236 Many lispers believe that there is no need for message passing
237 with generic methods and some advanced functional techniques,
238 but in a concurrent environment message passing becomes useful
239 again, especially when the communicating objects / actors are not
240 in the same address space.)
242 Normally in the actor model, we don't have direct references to
243 an actor, only an identifier.
244 This is for two reasons: to quasi-enforce the "shared nothing"
245 environment (actors absolutely control their own resources, and
246 "all you can do is send a message" to request that they modify
247 them) and because... well, you don't even know where that actor is!
248 Actors can be anything, and anywhere.
249 It's possible in 8sync to have an actor on a remote hive, which means
250 the actor could be on a remote process or even remote machine, and
251 in most cases message passing will look exactly the same.
252 (There are some exceptions; it's possible for two actors on the same
253 hive to "hand off" some special types of data that can't be serialized
254 across processes or the network, eg a socket or a closure, perhaps even
255 one with mutable state.
256 This must be done with care, and the actors should be careful both
257 to ensure that they are both local and that the actor handing things
258 off no longer accesses that value to preserve the actor model.
259 But this is an advanced topic, and we are getting ahead of ourselves.)
260 We have to supply the id of the receiving actor, and usually we'd have
262 But since in this case, since the actor we're sending this to is
263 ourselves, we have to pass in our identifier, since the Hive won't
264 deliver to anything other than an address.
266 Astute readers may observe, since this is a case where we are just
267 referencing our own object, couldn't we just call "sending a line"
268 as a method of our own object without all the message passing?
269 Indeed, we do have such a method, so we /could/ rewrite handle-line
273 (define-method (handle-line (irc-bot <my-irc-bot>) speaker channel
275 (irc-bot-send-line irc-bot channel
276 (format #f "Bawwwwk! ~a says: ~a" speaker line)))
279 ... but we want to get you comfortable and familiar with message
280 passing, and we'll be making use of this same message passing shortly
281 so that /other/ actors may participate in communicating with IRC
284 Anyway, our current message handler is simply too annoying.
285 What would be much more interesting is if we could recognize
286 when an actor could repeat messages /only/ when someone is speaking
288 Luckily this is an easy adjustment to make.
291 (define-method (handle-line (irc-bot <my-irc-bot>) speaker channel
293 (define my-name (irc-bot-username irc-bot))
294 (define (looks-like-me? str)
295 (or (equal? str my-name)
296 (equal? str (string-concatenate (list my-name ":")))))
297 (when (looks-like-me?)
298 (<- irc-bot (actor-id irc-bot) 'send-line channel
299 (format #f "Bawwwwk! ~a says: ~a" speaker line))))
302 This is relatively straightforward, but it isn't very interesting.
303 What we would really like to do is have our bot respond to individual
304 "commands" like this:
307 <foo-user> examplebot: hi!
308 <examplebot> Oh hi foo-user!
309 <foo-user> examplebot: botsnack
310 <examplebot> Yippie! *does a dance!*
311 <foo-user> examplebot: echo I'm a very silly bot
312 <examplebot> I'm a very silly bot
315 Whee, that looks like fun!
316 To implement it, we're going to pull out Guile's pattern matcher.
319 (define-method (handle-line (irc-bot <my-irc-bot>) speaker channel
321 (define my-name (irc-bot-username irc-bot))
322 (define (looks-like-me? str)
323 (or (equal? str my-name)
324 (equal? str (string-concatenate (list my-name ":")))))
325 (match (string-split line #\space)
326 (((? looks-like-me? _) action action-args ...)
328 ;; The classic botsnack!
330 (<- irc-bot (actor-id irc-bot) 'send-line channel
331 "Yippie! *does a dance!*"))
333 ((or "hello" "hello!" "hello." "greetings" "greetings." "greetings!"
334 "hei" "hei." "hei!" "hi" "hi!")
335 (<- irc-bot (actor-id irc-bot) 'send-line channel
336 (format #f "Oh hi ~a!" speaker)))
338 (<- irc-bot (actor-id irc-bot) 'send-line channel
339 (string-join action-args " ")))
341 ;; ---> Add yours here <---
345 (<- irc-bot (actor-id irc-bot) 'send-line channel
346 "*stupid puppy look*"))))
347 ;; Otherwise... just spit the output to current-output-port or whatever
350 (format #t "~a emoted ~s in channel ~a\n"
351 speaker line channel)
352 (format #t "~a said ~s in channel ~a\n"
353 speaker line channel)))))
356 Parsing the pattern matcher syntax is left as an exercise for the
359 If you're getting the sense that we could make this a bit less wordy,
363 (define-method (handle-line (irc-bot <my-irc-bot>) speaker channel
365 (define my-name (irc-bot-username irc-bot))
366 (define (looks-like-me? str)
367 (or (equal? str my-name)
368 (equal? str (string-concatenate (list my-name ":")))))
369 (define (respond respond-line)
370 (<- irc-bot (actor-id irc-bot) 'send-line channel
372 (match (string-split line #\space)
373 (((? looks-like-me? _) action action-args ...)
375 ;; The classic botsnack!
377 (respond "Yippie! *does a dance!*"))
379 ((or "hello" "hello!" "hello." "greetings" "greetings." "greetings!"
380 "hei" "hei." "hei!" "hi" "hi." "hi!")
381 (respond (format #f "Oh hi ~a!" speaker)))
383 (respond (string-join action-args " ")))
385 ;; ---> Add yours here <---
389 (respond "*stupid puppy look*"))))
390 ;; Otherwise... just spit the output to current-output-port or whatever
393 (format #t "~a emoted ~s in channel ~a\n"
394 speaker line channel)
395 (format #t "~a said ~s in channel ~a\n"
396 speaker line channel)))))
399 Okay, that looks pretty good!
400 Now we have enough information to build an IRC bot that can do a lot
402 Take some time to experiment with extending the bot a bit before
403 moving on to the next section!
404 What cool commands can you add?
406 ** An intermission: about live hacking
410 ** Adding a "rankings" web page
412 ** Writing our own <irc-bot> from scratch