Skip to content

Instantly share code, notes, and snippets.

@genericallyloud
Created March 17, 2012 08:45
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 genericallyloud/2056771 to your computer and use it in GitHub Desktop.
Save genericallyloud/2056771 to your computer and use it in GitHub Desktop.
Attempting to break <| between instancing and extending

There's something that's been nagging me about the <| operator ever since I first saw it. With the recent discussion (yet again) of a possible alternative syntax, I've been thinking a lot about it, and I think I've finally figured out what really bugs me (beyond the syntax itself).

The <| operator was born out of a few different use cases coming together (as listed in the proposal):

  • Specifying an explicit [[Prototype]] for object literals
  • Specifying an explicit [[Prototype]] for array literals
  • “Subclassing” arrays
  • Setting the prototype of a function to something other than Function.prototype
  • Implementing class-like parallel constructor and instance prototype inheritance chains.
  • Setting the prototype of RegExp and other built-in objects.
  • Replace the most common uses of the mutable __proto__ extension

While all of these use cases involve creating a new thing and setting its prototype, the intent can be broken down into a couple of conceptually different desires.

  1. Creating a new instance of a thing.
  2. Creating a "subclass" of a thing which can be used to make more instances

This is basically new vs extends, only the newing doesn't use a constructor function, and extends still doesn't actually exist. What I propose is that we take these two sides and come up a with a better solution for each instead of attempting to wrap them up into one thing.

Instancing

If we focus on the instancing use case for a moment, what we really want, is to be able to spin up alternative versions of the built-in types, but use them in the same way as we would other literals. Any of these literals have a way of being constructed imperatively, but we choose to use literals because of how expressive they are. I would suggest that we find a compact syntax like :

let o = MyObject.prototype <| {a:1,b:2}
//becomes
let o = MyObject:{a:1,b:2} //notice I also made it smart enough to drop the .prototype

someFunc(MyArr <| [0,1,2,3,4,5]);
//becomes
someFunc(MyArr:[0,1,2,3,4,5]);

var p = MyRegExp <| /[a-m][3-7]/
// becomes
var p = MyRegExp:/[a-m][3-7]/

It's not a huge difference in the number of characters, but I chose : because it works well without needing spaces around it, and feels to me more like an annotation of the parent type than an actual operator. Because I'm trying to go for something that's geared more towards "instance" use, I went with a sugary optimization - if the LHS is a function, get its .prototype.

I think with a lighter feeling syntax it might become a little more commonplace to be able to just use a subclass instead of monkeypatching when appropriate, especially if you chose shortened names.

let email = Email:"russell.leggett@gmail.com";
if(email.isValid){
    email.sendMessage({
        subject: "Hello!",
        body: "you won't find this on a normal string, dude"
    });
}

One little gem I was thinking of, too, was in regards to functions. Remembering for a moment that we're thinking about instances and not extensions, how would we want the syntax to look? Well, as nice as they do now (or better).

[1,2,3].map( MyFunc:function(num){return num * 2;} );

I thought about some of the short function proposals, and it struck me that there is a possibility for some crossover here. With this extension syntax, we wouldn't have any grammar issues with dropping the 'function' keyword because we know the rhs has to be a literal.

[1,2,3].map( MyFunc:(num){return num * 2;} );

That got me thinking that using that syntax, someone could just alias Function and have a very straightforward short function syntax.

let f = Function;
[1,2,3].map( f:(num){return num * 2;} );

Notice that this isn't the same behavior as <|. Because of the check for prototype rule, this would always extends a function's prototype and not the function itself, however, when it comes to instancing, this still makes sense.

That leads us into extending...

Extending

There's a couple of sticky problems when it comes to the extending use cases and the <| operator.

  1. No good way of declaring a name for your extension - has to be an assignment
  2. The new class extension pattern is a little cleaner, but almost as cryptic as the old way, and puts a bunch of special rules in <| when it comes to functions to enable it.

I'm really hoping for an actual class syntax to come and save us from this. I won't go into an actual class proposal here, but let's just say its something like this:

class Sub extends Sup {
    ...
}

Yay, now we don't need to worry about those special rules in <| and we have a way of declaring a name for our sub-thing. Also, let's just assume that it has support for extending built-ins:

class Email extends String {
    get isValid:function(){...}
    ...
}
//or
class MyFunc extends Function {
    curry(...args){...}
}
//etc.

This basically tackles the other half of the use cases. The only real remaining hole is extending functions:

function sub(arg1,arg2) extends sup {
    super(arg1,arg2);
    //more stuff here
}

Note - this doesn't try to do class magic, it just lets you easily extend a function from another function, because the : doesn't really let you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment