Skip to content

Instantly share code, notes, and snippets.

@genericallyloud
Created November 1, 2011 21:48
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save genericallyloud/1332028 to your computer and use it in GitHub Desktop.
Save genericallyloud/1332028 to your computer and use it in GitHub Desktop.
Unified class, <|, super, mixins proposal
// My take on unifying 3 concepts we currently have on the table:
// minimalist classes, triangle operator, and traits. This is mostly
// a rehash of existing ideas, with my own twist. Also - I will say upfront
// that the traits I'm proposing are for much simpler style ruby mixins
// because the traits proposal now are off the table.
// To start with, I'd like to advocate the minimalist approach that
// Jeremy Ashkenas proposed - the key element being (to me) being that
// it can take any expression for its definition. This is causing some
// debate right now, but I'll put that on the stack at the moment
// **************************
// PART I - minimal classes
// *************************
// Jeremy's initial example with slight modification to use some of the
// new object literal extensions
class Color {
constructor(hex) {
...
},
r: 1, g: 1, b: 1,
copy(color) {
...
},
setRGB(r, g, b) {
...
},
setHSV(h, s, v) {
...
}
}
// This is not so different from what has been proposed before but he goes
// on to include the expression forms:
class Student objectContainingStudentProperties
// Or even:
class Protester merge(YoungAdult, WorkEthic, Idealism, {
student: true
})
// To break this down, I would say that what is going on here is pretty interesting.
// class is effectively a special operator that does the magic of a constructor function
// in reverse, which strikes me as quite elegant. Sort of like the mathematical dual of new.
// where a constructor function
function Color(hex){
...
}
// creates a prototype and automatically maps a constructor property to it
// pointing back at the constructor function,
class Color {
constructor(hex){
...
}
}
// takes an object with a constructor property and creates a constructor
// function from it with the object as prototype
// one really nice side effect is that it can take the oExemplar form and
// convert it into the fExemplar form. This reduces the need for
// the new <Object> proposal, although I still like that form.
// *****************************************
// PART II - extension and the <| operator
// *****************************************
// It seems to me that the extends from class, and the <| operator
// are clearly related, and also that nobody likes the syntax of the
// <| operator right now. I would propose that we bring the two together
// Let me start with the beginning of the exemplar discussion.
// Allen provided these examples:
// Example A:
var SubFoo = Foo <| function (a,b,c) {
super.constructor(a,b);
this.z = c;
};
// Example B:
var SubBar = Bar <| {
z: 0,
constructor (a,b,c) {
super.constructor(a,b);
this.z = c;
}
}
// and even the possible combination of forms:
// Example C:
var SubFooLit = Foo <| {
z: 0,
constructor (a,b,c) {
super.constructor(a,b);
this.z = c;
}
}
//Example D:
var SubBarFunc = Bar <| function (a,b,c) {
super.constructor(a,b);
this.z = c;
};
// Personally, I find the most compelling cases here to be B and C,
// The ones with an object literal on the RHS. I find it pretty rare
// to ever want to extend a class just to override its constructor function.
// The problem is that the result of <| is the RHS, so these will both be object.
// You are probably seeing where I'm going with this. If we allow Jeremy's proposal of
// class expressions taking arbitrary expressions for the definition, we can bring these two
// together.
// Alternate Example C:
class SubFoo (Foo <| {
z: 0,
constructor (a,b,c) {
super.constructor(a,b);
this.z = c;
}
})
// Thats pretty close, but lacks a little in the way of sugar.
// Jeremey's proposal (among others) would look like
class SubFoo extends Foo {
z: 0,
constructor (a,b,c) {
super.constructor(a,b);
this.z = c;
}
}
// To me, this is just a change from
sup <| sub
// To
extends sup sub
// and that is what I would propose as the final syntax for <|
// so if we wanted to retain the original oExemplar result we could do
var SubFooLit = extends Foo {
z: 0,
constructor (a,b,c) {
super.constructor(a,b);
this.z = c;
}
}
// Finally, in regards to extends - to fully match Jeremy's proposal and
// be conceptually consistent, <| (or extends) needs to be able to take
// an expression on the RHS. Again, I will ask to put this on the stack
// because it seems to me that things are fitting into place pretty well.
// Note: I understand that the original purpose of <| was to be able to
// the prototype of a fresh literal expression, and that this purpose makes
// less sense with non-literals, but I think that changing it to be a more arbitrary
// extends operator would still work for the original purpose, even if the
// semantics differ a little. More on this in Part V
// ********************
// PART III - super
// ********************
// The discussion on Jeremy's proposal quickly turned into a discussion
// on dynamic vs static super. The case has been made for static, and I think that
// is fair enough. Jeremy has stated, and others agree, that super should not be tied to
// to classes, however, IMHO, I think it would make sense to be tied to extends. I know
// that Jeremy would like it to work with any prototypal hierarchy, but considering both
// extends and super are new, we don't have to worry about backwards compatibility. Also,
// if extends can take arbitrary expressions, then the new mechanism can be added to older
// libraries, and used in the same way.
// *************************
// PART IV - traits/mixins
// *************************
// Traits have been discussed recently, and that discussion along with Mark's(?) original
// proposal point to the traits.js library. It has been pointed out that while those traits
// are more robust than simple mixins like ruby. I'm pretty sure there's no way
// that traits.js style traits will make it into ES6, but what about the simpler mixins. Something
// like what Ruby has. It seems to me, that if we can crack the extends RHS-as-expression nut, then
// we would basically get mixins for free.
const Comparable = {
compareTo(other){
throw new Error("Must implement compareTo");
}
equals(other){
return this.compareTo(other) === 0;
}
greaterThan(other){
return this.comparTo(other) > 0;
}
...
}
class Person extends Comparable {
constructor(name,height){
this.name = name;
this.height = height;
}
compareTo(other){
return this.height - other.height;
}
}
let bill = new Person("Bill", 68);
let bob = new Person("Bob",72);
let billTaller = bill.greaterThan(bob);//false
// This is a simple example and does not require an arbitraty RHS expression, but let's
// imagine that we want to use Comparable more like a mixin. Lets say we would like to
// also extend a real superclass, but also mixin Comparable and Enumerable
class Sub extends Sup extends Comparable extends Enumerable {
...
}
// This would be impossible to do without arbitrary RHS expressions, but just happens if we allow it.
// Not sure if we'd want to sugar it up like Scala does:
class Sub extends Sup with Comparable, Enumerable {
...
}
// I use with here, but that would obviously clash a little with the existing with - perhaps just ,s
class Sub extends Sup, Comparable, Enumerable {
...
}
// ***************************
// PART V - non-literal RHS
// ***************************
// I've put this on the stack because I'm sure it'll be the part that gets the most argument. I'm
// hoping that if we can assume for a moment that this problem were solved that most of what I've
// proposed would be agreeable to many. Here goes nothing:
// I propose a new form of object wrapper that holds an object and a reference to another object
// that will act as that object's prototype. This would be strictly non-destructive, but allow it to
// be used in place of the original object.
// lets start with a <| example
function customExtend(parent, child){
//some special modification to child goes here
return parent <| child;
}
// or my proposed form
function customExtend(parent, child){
//some special modification to child goes here
return extends parent child;
}
// This would not currently work with <| and for good reason.
// With the current semantics, setting the _proto_ for child
// could be a BAD thing, and certainly would not work with my intended
// semantics for traits/mixins
// My idea is that instead of directly modifying child's _proto_, something like a special
// purpose direct proxy were used instead, where all method calls pass through to the wrapped object,
// but any references to the prototype, or going up the prototype chain would instead be delegated
// to the wrapper's specified prototype instead. This would also be what calls to super would refer to,
// as the two are directly related.
// I'm not sure if this would be easy to make efficient. My gut says it would be more efficient than
// copying object properties, and certainly seems like it would alleviate the concerns of things like
// copying private members. It would also ensure that in the mixin example, changes to the mixin would
// be reflected in the child. Not sure if that is a good thing, but its consistent with prototypal
// inheritance.
// In the common case of a literal RHS, this wrapper could be elided, and therefore create no
// additional overhead.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment