doc: Tweak and fix the "Writing our own actors" section.
[8sync.git] / doc / 8sync-new-manual.org
index 3e17cbf4387f48504551630efb734b8dee3c89ef..d2771c1108f627ef74234dda061663f9c94c30f9 100644 (file)
@@ -161,22 +161,22 @@ yet.  Time to fix that!
                   (channels '("##botchat")))
   (define hive (make-hive))
   (define irc-bot
                   (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
   (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
 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
 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
@@ -426,12 +426,12 @@ Redefine run-bot like so:
                     (repl-path "/tmp/8sync-repl"))
     (define hive (make-hive))
     (define irc-bot
                     (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
     (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)
                           #:path repl-path))
 
     (run-hive hive (list (bootstrap-message hive irc-bot 'init)
@@ -552,7 +552,7 @@ IRC> /query examplebot
 
 Horray!
 
 
 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?
 
 Let's write the most basic, boring actor possible.
 How about an actor that start sleeping, and keeps sleeping?
@@ -573,7 +573,7 @@ How about an actor that start sleeping, and keeps sleeping?
       (8sleep 1)))
 
   (let* ((hive (make-hive))
       (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
 
     (run-hive hive (list (bootstrap-message hive sleeper 'loop))))
 #+END_SRC
 
@@ -642,7 +642,7 @@ reference other actors.
                            (work-on-this worker-work-on-this))))
 
   (define (worker-work-on-this worker message difficulty)
                            (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)
     (set! (worker-task-left worker) difficulty)
     (display "worker> Whatever you say, boss!\n")
     (while (and (actor-alive? worker)
@@ -650,20 +650,19 @@ reference other actors.
       (display "worker> *huff puff*\n")
       (set! (worker-task-left worker)
             (- (worker-task-left worker) 1))
       (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
 #+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))
 
 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
 
     (run-hive hive (list (bootstrap-message hive manager 'assign-task 5))))
 #+END_SRC
 
@@ -675,7 +674,6 @@ worker> *huff puff*
 worker> *huff puff*
 worker> *huff puff*
 worker> *huff puff*
 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
 #+END_SRC
 
 "<-" pays no attention to what happens with the messages it has sent
@@ -701,7 +699,8 @@ into a micromanager.
     (<- (manager-direct-report manager)
         'work-on-this difficulty)
 
     (<- (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
     (manager-micromanage-loop manager))
 
   ;;; And add the following
@@ -709,25 +708,26 @@ into a micromanager.
   (define (manager-micromanage-loop manager)
     "Pester direct report until they're done with their task."
     (display "manager> Are you done yet???\n")
   (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")
           (begin (display "manager> Harumph!\n")
-                 (8sleep 1)
+                 (8sleep (/ 1 2))
                  (when (actor-alive? manager)
                  (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,
 #+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
 (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.
 
 
 Of course, we need to update our worker accordingly as well.
 
@@ -746,15 +746,50 @@ 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."
   ;;; 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!"
 
   (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
 
     (self-destruct worker))
 #+END_SRC
 
+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
 "<-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
@@ -777,3 +812,47 @@ Ka-poof!
 ** IRC
 ** Web / HTTP
 ** COMMENT Websockets
 ** 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!)
+