1) Download DrScheme from plt-scheme.org and install.
2) Select the Pretty Big language in DrScheme.
3) Download SchemeTalk from subversion or a prebuilt plt file, in which case you go to step 5.
4) Open make.ss in DrScheme and run. This will produce a schemetalk .plt file.
5) In DrScheme, click on File>Install .plt File
now SchemeTalk is setup and you are ready to go.
2. Getting Started
In order to load SchemeTalk into your Scheme environment, require schemetalk.
> (require schemetalk)
Now your environment is setup with the basics to start building classes and instances.
> (define my-class (new-class MyClass object-class () () () ()))
The previous snippet builds a class called “MyClass”, which is accessible in the current environment by use of the identifier my-class. The class has object-class as superclass, has no instance- nor class-variables nor -methods. Here we see that all top-level classes have object-class as default superclass.
Note that this is not obligatory. The only requirements are that
object is the superclass of the root-class as a marker and that the
doesnotunderstand method does something useful in case of an un-found selector. Using object-class as superclass is just an easy way to ensure that these requirements are met.
Now we can build instances of our class by using:
> (define instance (my-class 'new))
As you might have noticed, as in Smalltalk, my-class is bound to an object which responds to messages as any other object. Classes respond to the message new to build new instances. These instances will be linked back to its class as you would inspect. Objects and classes behave like functions would in regular scheme. Messages are sent by passing it as the [first argument] of the [call] of the object. Arguments of messages are passed as subsequent arguments.
Lets now have a look at our newly created instance:
> (instance 'inspect) ((class "MyClass class"))
When we send inspect to an instance, by default you get a shallow printout of all its instance variables. Since our class doesn’t have any declared instance variables, we just have the automatically inserted class instance variable.
Note that this instance variable can not be removed, it can only be reassigned to another class
Since classes are also objects, we can also send inspect to our class:
> (my-class 'inspect) ((class "MyClass-metaclass") (methoddictionary "Instance of MethodDictionary") (instance-variables ()) (superclass "Object class") (name MyClass))
Note that next to the textual inspector, there is also a graphical inspector pre-built around which allows you to browse through the object-graph:
> (require schemetalk/inspector)
Now lets create a new class “Person” with an instance variable “name”.
> (define person (new-class Person object-class (name) () ((initialize (name) (self 'set-name! name))) ()))
The first of four lists in the new-class construct lists the instance variables of a class. The second its class variables. Then the instance methods and finally the class methods. Our class has one instance variable name and one instance method initialize which takes one argument as input.
> (define me (person 'new "My Name"))
Here we see the first part of the extensive built-in meta-object protocol. Just like in Smalltalk, the default new method first calls basic-new on itself, and then calls initialize on the result of basic-new; the newly created instance. Unlike in Smalltalk, this method is allowed to take arguments. It is implemented as:
(define new-method (method args (let ((result (self 'basic-new))) (apply result (cons 'initialize args)) result)))
The second thing that can be noticed is that instance variables are not referenced by variable-names which are magically around in the correct scope, as in smalltalk, but by doing a self-send with the protocol set-[instance-variable-name]!. Getters are automatically installed as get-[instance-variable-name].
The only variables which are magically created in the scope of a method are self and super. Self is bound to the object to which the message was sent. Super is bound to an object encapsulating “self” and a pointer to the superclass of the class where the current method was found; as in other well-known object-oriented programming languages.
> (me 'inspect) ((class "Person class") (name "My Name"))
Using class variables and methods works in the same way as instance variables and methods. Class variables are stored per class. So if we have a subclass of a class with an instance variable, it will inherit the instance variable from its superclass, but it will not contain the same value as its superclass.
> (define person (new-class Person object-class () (sex) () ())) > (define male (new-class Male person () () () ((initialize () (self 'set-sex! "male"))))) > (male 'initialize) > (male 'get-sex) "male" > (person 'get-sex) un-initialized-slot2422
Here we see another part of the default behaviour of slots (variable holders of instances) in Schemetalk: if no value was set (and is thus not yet initialized), a special symbol is returned on accessing the slot, making that it is not yet initialized.
This can be tested by use of the function initialized?:
> (initialized? (person 'get-sex)) #f > (initialized? (male 'get-sex)) #t