(channels '("##botchat")))
(define hive (make-hive))
(define irc-bot
- (hive-create-actor* hive <my-irc-bot> "irc-bot"
- #:username username
- #:server server
- #:channels channels))
+ (bootstrap-actor* hive <my-irc-bot> "irc-bot"
+ #:username username
+ #:server server
+ #:channels channels))
(run-hive hive (list (bootstrap-message hive irc-bot 'init))))
#+END_SRC
Actors are connected to something called a "hive", which is a
special kind of actor that runs all the other actors.
Actors can spawn other actors, but before we start the hive we use
-this special "hive-create-actor*" method.
+this special "bootstrap-actor*" method.
It takes the hive as its first argument, the actor class as the second
argument, a decorative "cookie" as the third argument (this is
optional, but it helps with debugging... you can skip it by setting it
to #f if you prefer), and the rest are initialization arguments to the
-actor. hive-create-actor* passes back not the actor itself (we don't
+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
(repl-path "/tmp/8sync-repl"))
(define hive (make-hive))
(define irc-bot
- (hive-create-actor* hive <my-irc-bot> "irc-bot"
- #:username username
- #:server server
- #:channels channels))
+ (bootstrap-actor* hive <my-irc-bot> "irc-bot"
+ #:username username
+ #:server server
+ #:channels channels))
(define repl-manager
- (hive-create-actor* hive <repl-manager> "repl"
+ (bootstrap-actor* hive <repl-manager> "repl"
#:path repl-path))
(run-hive hive (list (bootstrap-message hive irc-bot 'init)
Horray!
-** Writing our own actors and sending messages between them
+** Writing our own actors
Let's write the most basic, boring actor possible.
How about an actor that start sleeping, and keeps sleeping?
(8sleep 1)))
(let* ((hive (make-hive))
- (sleeper (hive-create-actor hive <sleeper>)))
+ (sleeper (bootstrap-actor hive <sleeper>)))
(run-hive hive (list (bootstrap-message hive sleeper 'loop))))
#+END_SRC
(work-on-this worker-work-on-this))))
(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)
(display "worker> *huff puff*\n")
(set! (worker-task-left worker)
(- (worker-task-left worker) 1))
- (8sync (/ 1 3)))
- (display "worker> Looks like I'm done! Can I go home yet?\n"))
+ (8sleep (/ 1 3))))
#+END_SRC
The worker also contains familiar code, but we now see that we can
-call 8sync with non-integer real numbers.
+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 (hive-create-actor hive <worker>))
- (manager (hive-create-actor hive <manager>
- #:direct-report worker)))
+ (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
worker> *huff puff*
worker> *huff puff*
worker> *huff puff*
-worker> Looks like I'm done! Can I go home yet?
#+END_SRC
"<-" pays no attention to what happens with the messages it has sent
(<- (manager-direct-report manager)
'work-on-this difficulty)
- ;; call the micromanagement loop
+ ;; Wait a moment, then call the micromanagement loop
+ (8sleep (/ 1 2))
(manager-micromanage-loop manager))
;;; And add the following
(define (manager-micromanage-loop manager)
"Pester direct report until they're done with their task."
(display "manager> Are you done yet???\n")
- (let ((still-working
- (msg-val (<-wait (manager-direct-report manager)
- 'done-yet?))))
- (if still-working
+ (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)
+ (8sleep (/ 1 2))
(when (actor-alive? manager)
- (manager-micromanage-loop manager)))
- (begin (display "manager> Oh! I guess you can go home then.\n")
- (<- (manager-direct-report manager) 'go-home)))))
+ (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 msg-val.
+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 msg-receive, but that's a bit more complicated.)
+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.
;;; New procedures:
(define (worker-done-yet? worker message)
"Reply with whether or not we're done yet."
- (<-reply message
- (= (worker-task-left worker) 0)))
+ (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.")
+ (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.)
+
+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
+
"<-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
** 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