This guide gives a brief introduction to the basics of using Protolk.
You currently need Chicken Scheme to use Protolk. After you have
installed Chicken, you can install the latest release of Protolk by
chicken-install protolk. Or, if you want the absolute latest
revision of Protolk, you can clone the git repository then run the
chicken-install command from within the repository clone directory.
To use Protolk in your code:
(require-extension protolk protolk-stdpob protolk-syntax-send-brackets protolk-syntax-own-prop-at)
If you are using Protolk's syntax sugar and you want to compile your code, you will need to tell the compiler to load the syntax modules as compiler extensions:
csc -X protolk-syntax-send-brackets -X protolk-syntax-own-prop-at your-code.scm
If you don't want to use syntax sugar, you should not give give those
flags, and you should also omit the syntax modules from the
require-extension expression shown above.
Pobs are usually created by deriving from another, already existing
pob. Protolk comes with a standard pob, called
stdpob, which you can
use as the base for your own pobs.
(define point (send stdpob 'derive props: (list (list 'x 0) (list 'y 0)) methods: (list (list 'x (prop-reader 'x)) (list 'y (prop-reader 'y)))))
This sends the message
derive to the object
stdpob, along with two
methods, which specify what props
(properties) and methods the new pob should start with.
reacts to this message by creating and returning a new pob. The new
pob's base will be
stdpob, meaning that the new pob inherits props
and methods from
stdpob, in addition to the props and methods that
the pob contains within itself.
The primary way to interact with pobs is to send them messages. We
have already seen one example of this above, when we sent the message
When a pob receives a message, it looks to see if it either contains
or inherits a method with the same name as the message. If it does, it
invokes that method and passes it any extra arguments specified after
the message name, such as the
methods keyword arguments
in the example above.
To send a message to a pob, use the
(send TARGET-POB 'MESSAGE-NAME ARG1 ... ARGN)
If you are using the "protolk-syntax-send-brackets" extension, you can use square brackets "[ ]" to do exactly the same thing:
[TARGET-POB MESSAGE-NAME ARG1 ... ARGN]
Note that the message name is not quoted when using this syntax.
The usual way to define methods is using the
(define-method point (move! #!key (x 0) (y 0)) (set! (own-prop 'x) (+ (own-prop 'x) x)) (set! (own-prop 'y) (+ (own-prop 'y) y)) self)
The defines a new method on the pob
point, with the method name
move!, and taking two keyword arguments,
y. The body of
this method does three things:
xparameter to the value of the pob's
xprop, then sets the pob's
xprop to the result of that addition.
yparameter and the pob's
Note that even though this method was defined on the
if another pob inherits from
point, the method would apply to
that other pob's props, and return the other prop. In other words,
self refer to the pob who received the message, which
is not necessarily the pob that the method was defined on.
Because it's so common for a pob's methods to read and write that
pob's props, Protolk provides some syntax sugar to make it more
convenient. If you use the
protolk-syntax-own-prop-at extension, you
can write the example above like this:
(define-method point (move! #!key (x 0) (y 0)) (set! @x (+ @x x)) (set! @y (+ @y y)) self)
If a pob is derived from another pob (the "base"), the derived pob will inherit props and methods from the base. Inheritance works dynamically, whenever a pob needs to look up a prop or method in itself. The way it looks up the prop is fairly simple:
#<unspecified>(the value returned by
Note that the process might recurse up many levels of base pobs, until it either finds the prop value or reaches a pob with no base. So, each pob inherits props from all its ancestors, with props in the closest ancestor taking precedence over props with the same name in more distance ancestors.
When a pob sets the value of one of its props, it simply sets the new value of the prop in itself. No lookup is performed, and no other pobs are directly affected. However, the new value is immediately available to any pobs that inherit from the pob who set its own value.
Methods are inherited in the same way as props, and the lookup process
is very similar. The main difference is that if the lookup fails, the
original pob invokes its own
_method-missing method, which by
default causes the pob to signal an error saying that the requested
method was not found.
There is one other special feature with method inheritance: super
methods. Inside a method body, you can use the
super macro to
invoke the next method with the same name in the inheritance chain.
(define verbose-point [point derive]) (define-method verbose-point (move! #!key (x 0) (y 0)) (display "Moving by: ") (display (list x y)) (newline) (super x: x y: y) (display "Now at location: ") (display (list @x @y)) (newline))
This method would print a message, then invoke the
point, then print another message.
If you want to invoke the super method using all the same arguments
that were passed to the current method, you can use the
macro. So, you could write the above example like this:
(define-method verbose-point (move! #!key (x 0) (y 0)) (display "Moving by: ") (display (list x y)) (newline) (super*) (display "Now at location: ") (display (list @x @y)) (newline))
If you want to invoke the super method using a list of args, you can
apply-super macro, which is analogous to the standard Scheme
If you try to invoke the super method when there is no super method,
Protolk will signal an error. You can check whether there is a super
method by using the