Table of Contents

1 Introduction

Goblins documentation: https://docs.racket-lang.org/goblins/

1.1 Ooh shiny

>> Show off time traveling Terminal Phase here <<

1.2 A minor troll (sorry Carl)

"Goblins was really easy to do because Sussman and Steele have already proven that scheme / the lambda calculus and actors are the same"

I'm sorry I'm sorry please don't kick me out of Friam

1.3 A Goblins recipe

  • Defrost some E
  • Prepare Scheme by carefully peeling off toxic "set!" rind. Prefer the W7 brand, but Racket will do and is more easily found in supermarkets.
  • Place E and Scheme in blender, adding "become" as your actor state-management system
  • Pulse for 30 seconds
  • Pour into an iced glass, add a slice of lime, and enjoy

1.4 What does Goblins have?

  • An "Actor" library (not a language) built on top of Racket / Scheme!
    • (Could be ported to anything with lexical scope and weakmaps tho)
  • Vat / "communicating event loops" style
  • Quasi-functional object system!
  • Transactional updates and time travel!
  • Async programming with promises!
  • Synchronous programming too! (But only in the same vat!)

1.5 What's not ready yet

  • Fully distributed "machine to machine" stuff
  • Ocap-safe module system (e-maker style)
  • Ocap-safe macro system (DEFINITELY not ready, see cap-talk threads for current musings)
  • drivers

1.6 Goblins architecture

Usable on two layers:

  • Vats: One-turn-at-a-time event loops, set up ways to communicate with each other automatically.
  • Actormaps: a bit more low level, vats technically wrap these. You can make and play with them directly. Great for games!

1.6.1 Abstraction nesting dolls

;; .--- "borrowed" from agoric???
;; |           
;; |      .--- Chris: "How about I call this 'hive'?"
;; |      |    Ocap community: "We hate that"
;; |      |    Everyone else: "What's a 'vat' what a weird name"
;; |      |
;; |      |    .--- Not a new idea, but new to expose to users?
;; |      |    |
;; |      |    |         .--- Scheme already uses "-ref" everywhere
;; |      |    |         |
;; |      |    |         |      .--- Actually a few kinds of these...
;; |      |    |         |      |    most common is "mactor:local-actor"
;; |      |    |         |      |    but there are others
;; |      |    |         |      |
;; |      |    |         |      |      .--- Finally a term that is
;; |      |    |         |      |      |    unambiguous and well-understood
;; |      |    |         |      |      |
;; V      V    V         V      V      V
(machine (vat (actormap {refr: (mactor object)})))

1.6.2 High level diagram

"Chris stop copying Mark's homework"

.----------------------------------.         .----------------------.
|            Machine 1             |         |       Machine 2      |
|            =========             |         |       =========      |
|                                  |         |                      |
| .--------------.  .---------.   .-.       .-.                     |
| |    Vat A     |  |  Vat B  |   |  \______|  \_   .------------.  |
| |  .---.       |  |   .-.   | .-|  /      |  / |  |    Vat C   |  |
| | (Alice)----------->(Bob)----' '-'       '-'  |  |  .---.     |  |
| |  '---'       |  |   '-'   |    |         |   '--->(Carol)    |  |
| |      \       |  '----^----'    |         |      |  '---'     |  |
| |       V      |       |         |         |      |            |  |
| |      .----.  |       |        .-.       .-.     |  .------.  |  |
| |     (Alfred) |       '-------/  |______/  |____---( Carlos ) |  |
| |      '----'  |               \  |      \  |     |  '------'  |  |
| |              |                '-'       '-'     '------------'  |
| '--------------'                 |         |                      |
|                                  |         |                      |
'----------------------------------'         '----------------------'

Not shown here: vats wrapping actormaps, actormaps wrapping mactors

actor-id@vat-id

actor-id@vat-id[@machine-id[+hints]]

actor-id@machine-id

1.6.3 Notable things

  • "Extremely minor" caveat: machines don't exist yet (coming this month?)
  • Live references contain "vat connectors": compare with "eq?" to see if in main vat. Otherwise, vat connectors are procedures, use to talk to it
  • Multiple kind of mactors but this is a rabbit hole

1.7 Shut up and show me the code

Ok, ok!

2 A brief review of (Racket-flavored) Scheme

2.1 Some simple procedures

;; Everyone's favorite
(define (hello-world)
  "Hello, world!")

;; A procedure that returns an enclosed procedure
(define (make-greeter greeter-name)
  (lambda (your-name)
    (format "Greetings ~a, my name is ~a!" your-name greeter-name)))

;; Assignment
(define (make-counter)
  (define count 0)
  (lambda ()
    (set! count (add1 count))
    count))

2.2 Add methods?

  • Method dispatch from procedures?
  • Or procedures as objects with just-one-method?

We want to be schemey, so it's the former!

2.2.1 Doing it manually

;; Counter with very verbose manual dispatch
(define (make-method-counter)
  (define count 0)

  (define (get-method method-name)
    ;; Pattern matching
    (match method-name
      ['get-count
       (lambda ()
         count)]
      ['increment
       (lambda ([by-amount 1])
         (set! count (+ count by-amount)))]))

  (define (dispatcher method-name . args)
    (define method (get-method method-name))
    (apply method args))

  dispatcher)

Yuck, but it works

2.2.2 Some sugar

(require goblins/actor-lib/methods)

(define (make-method-counter2)
  (define count 0)
  (methods
   [(get-count)
    count]
   [(increment [by-amount 1])
    (set! count (+ count by-amount))]))

MUCH easier to read!

Rabbithole: Extending yer methods

  1. But it could be nicer still
    ;; meh, ok
    (a-vat 'call alice2 'name)
    (a-vat 'call alice2 'greet "Chris")
    
    ;; nice
    (a-vat.call alice2.name)
    (a-vat.call alice2.greet "Chris")
    

    Coming soon to a Racket #lang near you

3 Goblins synchronize a Scheme-takeover

3.1 It's dangerous to go alone

Take an actormap:

(require goblins)

(define am
  (make-actormap))

(NOTE: Most users won't use an actormap! They'll use a vat! Actormaps are lower level but I want to explain things to you all!)

3.2 Convert all previous examples to Goblins-style actors!

Topics to cover:

  • [X] Simple bcom-centric actors
  • [X] Adding cells
  • [X] Don't forget the methods one
  • [X] Actormap context procedures
    • [X] Oh hello there "spawn"
    • [X] Oh hello there "$" (pronounced "dollar call"… why?)
  • [X] Different actormap procedures (for bootstrapping)
    • [X] actormap-poke!
    • [X] actormap-peek
    • [X] actormap-run!
    • [X] actormap-turn
  • [X] whactormaps and transactormaps
  • [X] Transactions, aka "Errors? We forgot all about them!"

Rabbitholes:

3.3 Let's make our own cell

So simple I'll do it live!

(Writing that down didn't doom me or anything)

3.4 "bcom" is a sealer

And the syscaller / actormap has the unsealer, hidden in the mactor

3.5 Errors are no big deal

That is, if we use "bcom" instead of "set!"

3.6 Time check

Snapshotting time with snapshot-whactormap!

(Speaking of, this is Friam so is there even any time left by this point? We haven't even gotten to the asynchronous message passing stuff…)

4 Goblins go asynchronous

With our new friends, "<-" and "on"!

4.1 Vats, at a high level

Copying MarkM's homework again (this one Figure 14.2 from MarkM's dissertation), with minor adjustments:

         .-----------------------.
         |Internal Vat Schematics|
         '======================='

        stack           heap
         ($)         (actormap)
      .-------.----------------------. -.
      |       |                      |  |
      |       |   .-.                |  |
      |       |  (obj)         .-.   |  |
      |       |   '-'         (obj)  |  |
      |  __   |                '-'   |  |
      | |__>* |          .-.         |  |- actormap
      |  __   |         (obj)        |  |  territory
      | |__>* |          '-'         |  |
      |  __   |                      |  |
      | |__>* |                      |  |
      :-------'----------------------: -'
queue |  __    __    __              | -.
 (<-) | |__>* |__>* |__>*            |  |- event loop
      '------------------------------' -'  territory

4.2 Making some vats

It's still dangerous to go alone. But what if we do have friends, but in different places?

(require goblins)

(define a-vat
  (make-vat))
(define b-vat
  (make-vat))

4.3 A friend

(define (^friend bcom my-name)
  (lambda (your-name)
    (format "Hello ~a, my name is ~a!" your-name my-name)))

(define alice
  (a-vat 'spawn ^friend "Alice"))

Call Alice!

4.4 A friend that calls nearby friends

(define (^calls-friend bcom our-name)
  (lambda (friend)
    (define what-my-friend-said
      ($ friend our-name))
    (displayln (format "<~a>: I called my friend, and they said:"
                       our-name))
    (displayln (format "   \"~a\"" what-my-friend-said))))

(define archie
  (a-vat 'spawn ^calls-friend "Archie"))

Call Archie, who calls Alice!

4.5 A friend that calls far away friends

(define bob
  (b-vat 'spawn ^calls-friend "Bob"))

Call Bob who calls Alice!

… uhoh

4.6 Introducing <-

Let's prototype this:

(b-vat 'run
       (lambda ()
         (<- alice "Brenda")))

Ok, that gives us a "promise"… but how to use it?

Aha… "on"! (Equivalent to E "when")

(b-vat 'run
       (lambda ()
         (on (<- alice "Brenda")
             (lambda (alice-says)
               (displayln (format "Got from Alice: ~a" alice-says))))))

4.7 But what if there's errors?

Look at this fine coffee-sipping friend

(define (^this-is-fine bcom)
  (lambda ()
    (error "Everything is on fire")))

We can catch that:

(b-vat 'run
       (lambda ()
         (define its-fine-right
           (spawn ^this-is-fine))
         (on (<- its-fine-right)
             (lambda (_val)
               (displayln (format "<narrator> And everything was indeed fine: ~a"
                                  val)))
             #:catch
             (lambda (err)
               (displayln (format "<narrator> But it wasn't fine at all: ~a"
                                  err)))
             #:finally
             (lambda ()
               (displayln "And that was the story of whether things were fine.")))))

4.8 "on" can return promises too

(define ((^bakery bcom name) carb)
  (format "~a's signature ~a baking" name carb))

(define petite-oven-bakery
  (a-vat 'spawn ^bakery "The Petite Oven"))

(a-vat 'run
       (lambda ()
         (define smell-vow
           (on (<- petite-oven-bakery "croissants")
               (lambda (what-you-smell)
                 (format "You smell ~a.  Heavenly!!"
                         what-you-smell))
               #:promise? #t))
         (on smell-vow displayln)))

4.9 Promise chaining / pipelining is better

"Machines grow faster and memories grow larger. But the speed of light is constant and New York is not getting any closer to Tokyo." – MarkM in Robust Composition: Towards a Unified Approach to Access Control and Concurrency Control

4.9.1 A car factory

(define (^car-factory bcom company-name)
  (define ((^car bcom model color))
    (format "*Vroom vroom!*  You drive your ~a ~a ~a!"
            color company-name model))
  (define (make-car model color)
    (spawn ^car model color))
  make-car)

(define fork-motors
  (a-vat 'spawn ^car-factory "Fork"))

fork-motors makes cars on a-vat, but what if we want to make and call from a remote vat?

But nested promises are terrible

(b-vat 'run
       (lambda ()
         (on (<- fork-motors "Explorist" "blue")
             (lambda (our-car)
               (on (<- our-car)
                   displayln)))))

4.9.2 Promise chaining is better

(b-vat 'run
       (lambda ()
         (define car-vow
           (<- fork-motors "Explorist" "blue"))
         (define drive-noise-vow
           (<- car-vow))
         (on drive-noise-vow
             displayln)))

4.9.3 Blah blah network latency blah blah

B => A => B => A => B

VS

B => A => B

4.9.4 Errors are also contagious

(define (^lessgood-car-factory bcom company-name)
  (define ((^car bcom model color))
    (format "*Vroom vroom!*  You drive your ~a ~a ~a!"
            color company-name model))
  (define (make-car model color)
    (error "Your car exploded on the factory floor!  Ooops!")
    (spawn ^car model color))
  make-car)

(define forked-motors
  (a-vat 'spawn ^lessgood-car-factory "Forked"))

(b-vat 'run
         (lambda ()
           (define car-vow
             (<- forked-motors "Exploder" "red"))
           (define drive-noise-vow
             (<- car-vow))
           (on drive-noise-vow
               displayln
               #:catch
               (lambda (err)
                 (displayln (format "Caught: ~a" err))))))

4.10 So why even have $?

  • It's convenient when you can use it
  • Necessary for transactional / atomic turns (ocap money)

4.11 But why not coroutines

  • Goblins' predecessors (XUDD & 8sync, and first version of Goblins) all based on coroutines (under <<- operator, called "splitchronous communication")
  • "Interleaving hazards" section of MarkM's dissertation reified many background worries
  • async / await style "coroutine consent"
    • possible, maybe ocap safe if human misleading
    • haven't tried it yet
    • is it worth it? I don't know. E style promise pipelining/chaining feels "nice enough" while still visible in its hazardousness

5 Rage against the machines I haven't implemented yet

5.1 What would addressing look like?

Sturdyref like <actor-id>@<machine-id>[<hints>] or something

Wait! Why not @vat-id?

5.2 Other CapTP blah blah

I mean, maybe we should just do a presentation when it's ready

6 Rabbit holes (Goblins also welcome)

6.1 Syscaller

When running low-level actormap-turn, actormap-spawn, a "syscaller" context is set up so that "<-" and "spawn" work without passing around a dynamic context

6.1.1 Wait this looks like dynamism how dare you

Why yes, there is a dynamic "syscaller" parameter inside the actor context…

Hidden deep in goblins/core.rkt:

;;; Syscaller internals
;;; ===================

;; NEVER export this.  That would break our security paradigm.
(define current-syscaller
  (make-parameter #f))

6.1.2 A play starring me and the ocap police

Me: Officer, I can explain!

Officer: WHY DO WE HAVE BOTH ACTORMAP-SPAWN AND SPAWN WHAT'S GOING ON HERE!

Me: That's… that's just for bootstrapping! There's no dynamism inside the actor context, I promise! It's just to make it so that "spawn" and "<-" don't require passing around the syscaller!

Officer: Don't you know that dynamism is ambient authority and references are the only form of security?

Me: No wait! Just think of it like a new language layer! It's like E!

Officer: Didn't you say this was a library and not a language?

Me: I… we can add a language layer! It's technically possible! It's Racket! They love making languages!

Officer: I've seen enough. Take 'em away boys!

The police officer rolls out a yellow tape around the crime scene which says: ABSTRACTION BARRIER: DO NOT CROSS

6.1.3 Alternate design path: pasing around a syscaller

(IF YOU SEE THIS MESSAGE YOU ARE TOO DEEP, ESCAPE THE RABBITHOLE NOW)

  • Okay I guess if you have to (SwingSet does this I think)
  • It's ugly though (sorry not sorry)
  • Now we have two choices:
    • Syscaller plus hidden transactormap mutation (not a big design change)
    • Make syscaller a monad!
      • Now with extra ugly
      • Also this turns out to be insecure for some requirements!
        • Paying for a newspaper, reading it, and then pretending like you never paid
  • How about we just say "Goblins has an implicit monad"???
    • Yeah! I like it! Something something "that's why time travel works!"
    • It's arguably true and saying it confuses people too much for anyone to argue with me!
  • If we paper over this with a language layer even less people would question it!

6.2 Mactors

(struct mactor ())
(struct mactor:local mactor ())
;; TODO: Will this ever exist?  It might only ever be a symlink.
(struct mactor:remote mactor (vat-connid))


;;; Resolved things
;;; ---------------
;; once a local refr, always a local refr.
(struct mactor:local-actor mactor:local
  (handler become-unsealer become?))

;; Once encased, always encased.
;; TODO: Maybe we don't need mactors for this.  Maybe anything that's
;;   not a mactor is basically "encased"?  But having an official
;;   mactor type gives us a clearer answer I guess.
(struct mactor:encased mactor (val))
(struct mactor:remote-actor mactor:remote ())
(struct mactor:symlink mactor (link-to-refr))
;; Once broken, always broken.
(struct mactor:broken mactor (problem))

;;; Eventual things
;;; ---------------
(struct mactor:local-promise mactor:local
  (listeners resolver-unsealer resolver-tm?))
(struct mactor:remote-promise mactor:remote ())

6.3 Don't actors need to be able to know their own id?

  • Not usually.
  • But they can! Lexical scope to the rescue!
  • Promise handlers reduce this need a lot… (have we gotten to "on" yet? How behind on time are we???)

6.4 Extending yer methods

  • [ ] Extending procedures
  • [ ] Extending actors
  • [ ] Racket-style mixins as a natural outgrowth
  • [ ] Selfish actors

6.5 What about Goblins and Agoric/SwingSet/Jessie

"Yeah, what about it buddy???"

  • I dunno? I have a hard time being motivated writing any non-lisps and I have a comfortable toolkit here
  • Also I started this before SwingSet was announced and thought I had interesting ideas I was exploring and wanted to Get It Out There
  • Obviously the Agoric folks Actually Know Things and I'm just this excited nerd on the internet so probably they know better than I do about basically everything imposter syndrome goes here
  • Yes Jessie is probably the most scheme-like thing that exists in JS and is the best way of reaching people until WASM opens things up for every language (and yes I know that's a ways off, GC proposal, delimited continuations proposal, WASI, etc)
  • Maybe Goblins should be ported to Jessie? I'd like to do it though I want to get the inter-machine stuff working yet. I am trying to figure out if I can work it into my schedule
  • Let's collaborate on the CapTP thingamabob!
    • Yes actually that sounds great
    • Though I will note that I'm very intimidated by IBC
    • Overcome'able?

6.6 Want to look at some code?

  • Goblins internals
  • Terminal Phase

7 Thanks

You all pretty much know this space better than I do I think

I hope you enjoyed this and I've contributed some ideas of interest

Thank you for your time! Seriously!

Author: Christopher Lemmer Webber

Created: 2020-03-06 Fri 16:09

Validate