-
Notifications
You must be signed in to change notification settings - Fork 0
Getting Started
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
running 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
keyword arguments, props
and methods
, which specify what props
(properties) and methods the new pob should start with. stdpob
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
derive
to stdpob
.
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 props
and methods
keyword arguments
in the example above.
To send a message to a pob, use the send
procedure:
(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
macro:
(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, x
and y
. The body of
this method does three things:
- It adds the value of the
x
parameter to the value of the pob'sx
prop, then sets the pob'sx
prop to the result of that addition. - Does the same thing for the
y
parameter and the pob'sy
prop. - Returns the pob.
Note that even though this method was defined on the point
pob,
if another pob inherits from point
, the method would apply to
that other pob's props, and return the other prop. In other words,
own-prop
and 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:
- If the pob itself contains a value for the prop it is looking for, it uses that value.
- Otherwise, if the pob has a base, it asks the base to look up the prop. The lookup recurses in the base.
- If the pob has no base and hasn't found the prop yet, it returns
#<unspecified>
(the value returned by(void)
.
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 move!
method
inherited from 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 super*
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
use the apply-super
macro, which is analogous to the standard Scheme
procedure, apply
.
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 super?
procedure.