Created
March 5, 2012 21:41
-
-
Save creationix/1981289 to your computer and use it in GitHub Desktop.
OOP proposals for the candor language
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// A simple example of how to do object oriented programming in candor using | |
// constructor functions with properties as the prototype. | |
// | |
// Here we assume ... argument sugar and : calling/defining sugar for | |
// functions. These are not in the language yet. `...` is the rest of the args | |
// when used in a function definition and when put at the end of a call, | |
// passes them on as multiple args. having a `:` in a function call or | |
// definition implies an implicit self first argument that is the object | |
// getting referenced from | |
// | |
// Given the following object: | |
// | |
// a = { value: 42 } | |
// | |
// The following three function definitions are equal. | |
// | |
// a.getValue = (self) { return self.value } | |
// a.getValue(self) { return self.value } | |
// a:getValue() { return self.value } | |
// | |
// Then the `:` can be used for calling too. | |
// | |
// a:getValue() | |
// | |
// which is the same as: | |
// | |
// a.getValue(a) | |
// | |
// That's the rough idea, we need to check the grammer implications of this. | |
// This object is the base class | |
Object = {} | |
Object:clone(self) { | |
// self here is the prototype object or class | |
// we just need to clone outself into a new object | |
clone = {} | |
keys = keysof self | |
for (i = 0, length = sizeof keys; i < length; i++) { | |
scope self, clone, keys, i | |
key = keys[i] | |
clone[key] = prototype[key] | |
} | |
return clone | |
} | |
// Create a clone of self and optionally call the constructor if there is one | |
Object:new(...) { | |
obj = self:clone() | |
obj.class = self | |
if (obj.initialize) obj:initialize(...) | |
return obj | |
} | |
// Rectangle is a class of shapes with w and h that compute area. | |
Rectangle = Object:clone() | |
Rectangle:initialize(w, h) { | |
self.w = w | |
self.h = h | |
} | |
Rectangle:getArea() { | |
return self.w * self.h | |
} | |
// Square has a custom constructor, but inherits the getArea method. | |
Square = Rectangle:clone() | |
Square:initialize(s) { | |
self.w = s | |
self.h = s | |
} | |
// Creating a Square | |
square = Square:new(4) | |
square:getArea() | |
// -> 16 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// This is like the luvit-style example, but with less syntax sugar and with a | |
// global helper function. The other pros and cons still apply. | |
// Simple helper for object calls | |
call(object, key, ...) { | |
return object[key](object, ...) | |
} | |
Object = {} | |
Object.clone = (self) { | |
// self here is the prototype object or class | |
// we just need to clone outself into a new object | |
clone = {} | |
keys = keysof self | |
for (i = 0, length = sizeof keys; i < length; i++) { | |
scope self, clone, keys, i | |
key = keys[i] | |
clone[key] = self[key] | |
} | |
return clone | |
} | |
} | |
// Create a clone of self and optionally call the constructor if there is one | |
Object.new = (self, ...) { | |
obj = call(self, "clone") | |
if (obj.initialize) call(obj, "initialize", ...) | |
return obj | |
} | |
Rectangle = call(Object, "clone") | |
Rectangle.initialize = (self, w, h) { | |
self.w = w | |
self.h = h | |
} | |
Rectangle.getArea = (self) { | |
return self.w * self.h | |
} | |
Square = call(Rectangle, "clone") | |
Square.initialize = (self, s) { | |
self.w = s | |
self.h = s | |
} | |
rect = call(Rectangle, "new", 3, 4) | |
call(rect, "getArea") | |
// -> 12 | |
square = create(Square, 5) | |
call(square, "getArea") | |
// -> 25 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// This is a fairly simple example. It still uses the `...` syntax extension | |
// so that creation and construction/setup can be in one call. | |
// Two sample prototypes | |
Rectangle = { | |
initialize: (self, w, h) { | |
self.w = w | |
self.h = h | |
}, | |
getArea: (self) { | |
return self.w * self.h | |
} | |
} | |
Square = { | |
initialize: (self, s) { | |
self.w = s | |
self.h = s | |
}, | |
getArea: Rectangle.getArea | |
} | |
// Two helpers for prototype based OOP | |
// This creates dynamic objects that are very fast to create. The syntax to | |
// call methods is slightly ugly and there is an extra property lookup cost | |
// and an extra function call with each call. | |
createDynamic(prototype, ...) { | |
obj = {} | |
obj.call = (name, ...) { | |
scope prototype, obj | |
return prototype[name](obj, ...) | |
}} | |
if (prototype.initialize) obj.call("initialize", ...) | |
return obj | |
} | |
// This creates a static object. New methods added to the prototype won't be | |
// seen. The syntax for using this object is very straightforward. There is | |
// an extra function call cost for the binding wrapper for each call. | |
createStatic(prototype, ...) { | |
obj = {} | |
// Bind the methods staticly to this instance | |
keys = keysof prototype | |
for (i = 0, length = sizeof keys; i < length; i++) { | |
scope prototype, obj, keys, i | |
key = keys[i] | |
obj[key] = (...) { | |
scope obj, prototype | |
return prototype[key](obj, ...) | |
} | |
} | |
if (obj.initialize) obj.initialize(...) | |
return obj | |
} | |
// Using createDynamic | |
rect = createDynamic(Rectangle, 3, 4) | |
rect.call("getArea") | |
// -> 12 | |
// Using createStatic | |
rect = createStatic(Rectangle, 3, 4) | |
rect.getArea() | |
// -> 12 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Here we have two class helpers. One creates a class that makes dynamic | |
// objects where creationin is fast and method lookup is dynamic. The other | |
// creates classes that create static objects. These bind all methods to the | |
// instance for easy use, but don't see new methods added to the class after the | |
// fact. | |
// Define a dynamic class (fast start, dynamic prototype) | |
dynamicClass(methods) { | |
methods.new = (...) { | |
scope methods | |
obj = { | |
call: (name, ...) { | |
scope obj, methods | |
return methods[name](obj, ...) | |
} | |
} | |
if (methods.initialize) methods.initialize(obj, ...) | |
return obj | |
} | |
return methods | |
} | |
// Define a static class (slower creation, uses more ram, but easier method calling.) | |
staticClass(methods) { | |
methods.new = (...) { | |
scope methods | |
obj = {} | |
keys = keysof methods | |
for (i = 0, length = sizeof keys; i < length; i++) { | |
scope methods, obj, keys, i | |
key = keys[i] | |
method = methods[key] | |
obj[key] = (...) { | |
scope obj, method | |
return method(obj, ...) | |
} | |
} | |
if (methods.initialize) methods.initialize(obj, ...) | |
return obj | |
} | |
return methods | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
Rectangle = dynamicClass({ | |
initialize: (self, w, h) { | |
self.w = w | |
self.h = w | |
}, | |
getArea: (self) { | |
return self.w * self.h | |
} | |
}) | |
Square = staticClass({ | |
initialize: (self, s) { | |
self.w = w | |
self.h = h | |
}, | |
getArea: Rectangle.getArea | |
}) | |
rect = Rectangle.new(2, 4) | |
rect.call("getArea") | |
// -> 8 | |
square = Square.new(5) | |
square.getArea() | |
// -> 25 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment