Plof Object System
Plof has a prototype-based object system. This means that (if you're used to class-based systems like C++, Java, etc.) objects are instantiated differently. A class does not exist; instead, a single object representing the class is created and cloned. Fear not if that sounds a bit scary, because Plof is, at least for a prototype-based language, not too different from class-based systems. Here is a simple example:
var SimpleObj = Object : [
data = Null
this(integer as NativeInteger) {
data = integer
}
getData = {
data
}
]The syntax for creating an object is Parent : [ ... ] (or for multiple inheritance, Parent1 : Parent2 : ... : [ ... ]). The parent, whose fields the object is given by inheritance, is in this case Object. Object is (similarly to Java) the parent, directly or indirectly, of every object.
The next thing to notice about this declaration is that variables and functions do not need the "var" keyword. "var" is assumed in object declarations by default. Null is a special value that you probably know of from other languages.
That would seem to be everything, but now we have to look at the this() method (by the way, it doesn't need = after its declaration as regular methods do). Every object has a special init() method which sets up the object (similar to a constructor in other languages), and this() executes its code and then returns this. So these methods are equivalent:
this(arg) {
doStuff arg
}
init = (arg) {
doStuff arg
this
}Now let's try an example, staying on the "Hello, world!" theme. Let's start with an object named "Greeting":
var Greeting = Object : [
greeting = Null
thing = Null
this(greeting as String, thing as String) {
this.greeting = greeting
this.thing = thing
}
print = {
Stdout.write(greeting ~ ", " ~ thing ~ "!\n")
}
]As you can see, this is not only a method, it is also a reference to the current object. Now let's try using object Greeting to write a "Hello, world!" program:
var helloWorld = new Greeting("Hello", "world")
helloWorld.print()new is a special function for creating an object, meant to mimic a class-based system. This is its complete code:
var new = (type) {
var robj = type:[]
robj.parent = type.parent
robj.init
}That's actually the exact code used for new. You'll see, in later tutorials, that a lot of operators that would seem to be built-in language constructs are really functions, and the constructs that are part of the grammar can still be modified and extended -- this is perhaps the most interesting and exciting part of Plof.
Now back to new(). When an object is instantiated with new(), it is actually a new object with "type" as its parent -- the next line of code sets its parent to type's parent. The final line of code initializes the object.
One last thing about objects -- operator overloading. This means that an object can have methods that override certain operator behaviours. Here's an example involving object Greeting:
Greeting := [
opAdd = (other as Greeting) {
new Greeting(greeting ~ " and " ~ other.greeting, thing ~ " and " ~ other.thing)
}
]The first thing to notice is := [ ... ], which simply adds fields or methods to an object. opAdd() is one of the special functions that overloads an operator, and is used like so:
var helloWorld = new Greeting("Hello", "world")
var salutationsUniverse = new Greeting("Salutations", "universe")
var bothGreetings = helloWorld + salutationsUniverse
bothGreetings.print()Here is a complete list of operators that can be overloaded (from the specification):
"x || y" -- x.opOr(y) "x && y" -- x.opAnd(y) "x == y" -- x.opEqual(y) "x != y" -- x.opNotEqual(y) "x < y" -- x.opLess(y) "x <= y" -- x.opLessEqual(y) "x > y" -- x.opGreater(y) "x >= y" -- x.opGreaterEqual(y) "x + y" -- x.opAdd(y) "x - y" -- x.opSub(y) "x * y" -- x.opMul(y) "x / y" -- x.opDiv(y) "x % y" -- x.opMod(y) "!x" -- x.opNot() "x as y" -- x.opAs(y) "x is y" -- x.opIs(y) "x in y" -- y.opContains(x) (note the reversed direction) "x[y]" -- x.opIndex(y)
And here are some special cases that can be used as well:
"x < y" -- x.opCmp(y) < 0 "x <= y" -- x.opCmp(y) <= 0 "x > y" -- x.opCmp(y) > 0 "x >= y" -- x.opCmp(y) >= 0 "x as y" -- Multiple steps: 1. If x is derived from y (determined by opIs), returns x. 2. x.opCastTo(y) 3. if opCastTo returned null, y.opCastFrom(x) 4. if opCastFrom returned null, null
Most of these are pretty self-explanatory. The "is" operator checks if an object is of a certain type. "in" checks if something is in an object, and "[...]" is generally for array types.
Well, that's pretty much it for objects. In the next tutorial we'll look at how Plof's grammar can be altered.