Skip to content

Instantly share code, notes, and snippets.

@creationix
Created March 5, 2012 21:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save creationix/1981289 to your computer and use it in GitHub Desktop.
Save creationix/1981289 to your computer and use it in GitHub Desktop.
OOP proposals for the candor language
// 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 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 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
// 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