Next: , Up: Objects   [Contents]


5.1.1 Spawning objects

Objects can be spawned with the spawn operator:

(spawn CONSTRUCTOR ARGS ...)
;=> <local-refr>

where CONSTRUCTOR is a procedure which itself returns a procedure representing an object’s current behavior. The CONSTRUCTOR is implicitly passed a first argument, traditionally labeled bcom, which can be used to change an object’s behavior. The ARGS are passed as the remaining arguments to the constructor.

So for example, let’s say we have the following constructor:

(define* (^cell bcom #:optional val)  ; constructor (outer procedure)
  (case-lambda                        ; behavior    (inner procedure)
    (()                               ; 0-argument invocation (getter)
     val)                             ;   (return current value)
    ((new-val)                        ; 1-argument invocation (setter)
     (bcom (^cell bcom new-val)))))   ;   ("become" ^cell with new value)

And so…

(define some-cell (spawn ^cell 42))

will bind val to 42.

Let’s look at the bcom argument in depth. This is a capability which allows the actor to change its own behavior. It must be called in a tail position, since bcom actually returns a datastructure demonstrating that this object is choosing to change its value (part of Goblins’ quasi-functional design.)

bcom also can take a second argument, which will be returned from the object’s invocation as a return value, allowing for both an object change its own behavior and return a value at the same time during an invocation.

The following actor represents a consumable object that returns a string explaining its current status:

(define (^drink bcom drink-name portions)
  (define (drinkable-beh portions)   ; "-beh" is a common suffix for "-behavior"
    (lambda ()
      (define portions-left
        (- portions 1))
      (if (zero? portions-left)
          (bcom empty-beh
                (format #f "*GLUG!* You drink your ~a. It's empty!"
                        drink-name))
          (bcom (drinkable-beh portions-left)
                (format #f "*GLUG!* You drink your ~a. Still some left!"
                        drink-name)))))
  (define (empty-beh)
    "Sadly, your glass appears to be empty!")
  (drinkable-beh portions))

Now, playing around at the REPL, we can see how this works:

> ,vr (define root-beer (spawn ^drink "root beer" 3))
> ,vr ($ root-beer)
=> "*GLUG!* You drink your root beer. Still some left!"
> ,vr ($ root-beer)
=> "*GLUG!* You drink your root beer. Still some left!"
> ,vr ($ root-beer)
=> "*GLUG!* You drink your root beer. It's empty!"
> ,vr ($ root-beer)
=> "Sadly, your glass appears to be empty!"

As you can see above, bcom is also a handy way to construct state machines, as ^drink moves from drinkable to empty as it is consumed.


Next: , Up: Objects   [Contents]