Next: , Up: Objects   [Contents][Index]


5.1.1 Object construction

Objects can be spawned with the spawn operator:

Procedure: spawn constructor args …

Construct and return a reference to a new object.

  • constructor: Procedure which returns a procedure representing the object’s initial behavior; implicitly passed a first argument, bcom, which can be used to change the object’s behavior.
  • args: Remaining arguments passed to constructor.

For example, given 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)

The instantiation…

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

will bind val to 42.

The bcom argument warrants further explanation. It is a capability which allows the actor to change its own behavior. It must be called in a tail position, since bcom returns a datastructure demonstrating that this object is choosing to change its value (part of Goblins’ quasi-functional design).

bcom can also take a second argument which will be returned from the object’s invocation as a return value. This allows an object to both change its behavior and return a value with the same 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))

Here’s how this works at the REPL:

> ,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.

spawn also has a few variants:

Procedure: spawn-named name constructor args …

Like spawn, and set the object’s debug name to name.

Procedure: spawn-promise-values

Return a promise and its associated resolver.

(define-values a-promise a-resolver (spawn-promise-values))
Procedure: spawn-promise-cons

Return a promise and its associated resolver as a cons pair.

(define promise-pair (spawn-promise-cons))
(define a-promise (car promise-pair))
(define a-resolver (cdr promise-pair))

Next: Synchronous calls, Up: Objects   [Contents][Index]