doc: Add a note about the calls to display.
[8sync.git] / doc / 8sync-new-manual.org
index d0a99673891d0482bb522d00370574522f332bb9..bd6bb319fad467e154d23b77122209099d523d22 100644 (file)
@@ -161,22 +161,22 @@ yet.  Time to fix that!
                   (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
@@ -205,7 +205,7 @@ Change handle-line to this:
 #+BEGIN_SRC scheme
   (define-method (handle-line (irc-bot <my-irc-bot>) speaker channel
                               line emote?)
-    (<- irc-bot (actor-id irc-bot) 'send-line channel
+    (<- (actor-id irc-bot) 'send-line channel
         (format #f "Bawwwwk! ~a says: ~a" speaker line)))
 #+END_SRC
 
@@ -295,7 +295,7 @@ Luckily this is an easy adjustment to make.
       (or (equal? str my-name)
           (equal? str (string-concatenate (list my-name ":")))))
     (when (looks-like-me?)
-      (<- irc-bot (actor-id irc-bot) 'send-line channel
+      (<- (actor-id irc-bot) 'send-line channel
           (format #f "Bawwwwk! ~a says: ~a" speaker line))))
 #+END_SRC
 
@@ -327,22 +327,22 @@ To implement it, we're going to pull out Guile's pattern matcher.
        (match action
          ;; The classic botsnack!
          ("botsnack"
-          (<- irc-bot (actor-id irc-bot) 'send-line channel
+          (<- (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!")
-          (<- irc-bot (actor-id irc-bot) 'send-line channel
+          (<- (actor-id irc-bot) 'send-line channel
               (format #f "Oh hi ~a!" speaker)))
          ("echo"
-          (<- irc-bot (actor-id irc-bot) 'send-line channel
+          (<- (actor-id irc-bot) 'send-line channel
               (string-join action-args " ")))
 
          ;; --->  Add yours here <---
 
          ;; Default
          (_
-          (<- irc-bot (actor-id irc-bot) 'send-line channel
+          (<- (actor-id irc-bot) 'send-line channel
               "*stupid puppy look*"))))))
 #+END_SRC
 
@@ -360,7 +360,7 @@ you're right:
       (or (equal? str my-name)
           (equal? str (string-concatenate (list my-name ":")))))
     (define (respond respond-line)
-      (<- irc-bot (actor-id irc-bot) 'send-line channel
+      (<- (actor-id irc-bot) 'send-line channel
           respond-line))
     (match (string-split line #\space)
       (((? looks-like-me? _) action action-args ...)
@@ -426,12 +426,12 @@ Redefine run-bot like so:
                     (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)
@@ -503,7 +503,7 @@ things to:
                               line emote?)
     ;; [... snip ...]
     (define (respond respond-line)
-      (<- irc-bot (actor-id irc-bot) 'send-line (pk 'channel channel)
+      (<- (actor-id irc-bot) 'send-line (pk 'channel channel)
           respond-line))
     ;; [... snip ...]
     )
@@ -533,7 +533,7 @@ to looks like our own username that we respond back to the sender.
                               line emote?)
     ;; [... snip ...]
     (define (respond respond-line)
-      (<- irc-bot (actor-id irc-bot) 'send-line
+      (<- (actor-id irc-bot) 'send-line
           (if (looks-like-me? channel)
               speaker    ; PM session
               channel)   ; normal IRC channel
@@ -552,7 +552,7 @@ IRC> /query examplebot
 
 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?
@@ -573,7 +573,7 @@ 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
 
@@ -615,7 +615,7 @@ Time to get back to work!
   (define (manager-assign-task manager message difficulty)
     "Delegate a task to our direct report"
     (display "manager> Work on this task for me!\n")
-    (<- manager (manager-direct-report manager)
+    (<- (manager-direct-report manager)
         'work-on-this difficulty))
 #+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 one task until done."
     (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))
-      (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
 
@@ -675,7 +674,6 @@ 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
@@ -698,10 +696,11 @@ into a micromanager.
   (define (manager-assign-task manager message difficulty)
     "Delegate a task to our direct report"
     (display "manager> Work on this task for me!\n")
-    (<- manager (manager-direct-report manager)
+    (<- (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
@@ -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")
-    (let ((still-working
-           (msg-val (<-wait manager (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 (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.
 
@@ -746,15 +746,54 @@ 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 worker 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
@@ -777,3 +816,47 @@ Ka-poof!
 ** 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!)
+