Obliq is a simple yet powerful object oriented interpreted language. While it is useful as an embedded interpreter and as a distributed language, it is an interesting, prototype based, programming language in its own rights. The language is described in the reference manual entitled Obliq A language with distributed scope.
In Obliq, each object contains a number of data fields and methods. There are no classes, methods table or inheritance defined in the language. Nonetheless, through cloning and delegation, it is possible to implement single and multiple inheritance, instance and class variables as well as dynamically changing the methods for an instance or for a class.
A class is implemented by defining a prototype object. This prototype object is then cloned in order to obtain instances. The memory usage drawback is that a full object is used to represent the class while a declaration of the attribute names and types would have been sufficient in a non prototype based language. Furthermore, each object stores a method table instead of simply pointing to a shared method table.
let CircleClass = { color => [0.0,0.5,0.0], origin => 0.0, radius => 0.0, draw => meth(self,window) ... end }; var c1 = clone(CircleClass), c2 = clone(CircleClass); c1.radius := 1.0; c2.radius := 2.0;
Single or multiple inheritance is easily obtained by cloning one or more parent class prototypes to create a child class prototype.
let RootClass = { name => ok, store => meth(self,file) ... end }; let ShapeClass = { color => [0.0,0.5,0.0], origin => 0.0 }; let CircleFields = { radius => 0.0, draw => meth(self,window) ... end }; let CircleClass = clone(RootClass, ShapeClass, CircleFields);
It is possible to update the fields and methods of each instance. Updating a data field or method in a class prototype will not affect the existing instances of this class but only the instances created after the update.
Even though each object (instance) has its fields and methods, it is possible through delegation to have class variables and methods.
let RootFields = { name => ok, store => meth(self,file) ... end }; let ShapeFields = { nbShapes => 0; color => Blue, origin => 0.0 }; let CircleFields = { radius => 0.0, draw => meth(self,window) ... end }; let CircleClass = clone(RootFields, ShapeFields, CircleFields); CircleClass.store := alias store of RootFields; CircleClass.draw := alias draw of CircleFields; CircleClass.nbShapes := alias nbShapes of ShapeFields;
In this example, the store, draw and nbShapes fields are delegated to the class fields holders. Therefore, all objects created by cloning CircleClass share these fields. Whenever the store method of RootFields is updated, for example, all the instances of CircleClass are affected and will access the updated method.