Next: Synchronous invocation, Up: Objects [Contents]
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: Synchronous invocation, Up: Objects [Contents]