Tales from the Evil Empire

Bertrand Le Roy's blog

News


Bertrand Le Roy

BoudinFatal's Gamercard

Tales from the Evil Empire - Blogged

Blogs I read

My other stuff

Archives

Mixins for Atlas

I've beeen toying with that idea for a while and I thought I would try to get some feedback on it. Mixins are a way to dynamically add members to an existing object. I've built a small function, Type.Mixin, that does that by copying members from the mixin object's prototype to the object that you want to extend. The mixin object is a type (that is, a function) that can't be instantiated (you can ensure that by throwing from the constructor). The members that will extend the target object are defined on the prototype of the mixin.

Here's an example of a mixin object that is specialized at extending strings:

Bleroy.Mixin.StringHtmlExtensions = function() {
    throw new Error("Can't instantiate a mixin.");
}
Bleroy.Mixin.StringHtmlExtensions.prototype = {
    // Don't do this at home, this is a very naive
    // implementation just for demonstration purposes.
    htmlEncode: function() {
        return this
            .replace(/&/g, "&")
            .replace(/\</g, "&lt;")
            .replace(/\>/g, "&gt;")
            .replace(/\"/g, "&quot;");
    },
    htmlDecode: function() {
        return this
            .replace(/&lt;/g, "<")
            .replace(/&gt;/g, ">")
            .replace(/&quot;/g, '"')
            .replace(/&amp;/g, "&");
    }
}
Bleroy.Mixin.StringHtmlExtensions.targetType = String;

You can extend a string with this mixin this way:

var val = new String($("userInput").value);
Type.Mixin(val, Bleroy.Mixin.StringHtmlExtensions);
$("encoded").innerHTML = val.htmlEncode();

Here, we get the value of an input field, apply the mixin and we're ready to use the mixin's methods on the string and set a label's text with the results. Note here that you need to construct the string instance using the String constructor. A string literal won't do because in JavaScript you can't extend objects of the built-in types that were not constructed from the corresponding type's constructor. You need to "box" the literal in order to be able to extend it.

Now, if you want to extend all string instances instead of just one, just pass the String prototype as the object to extend:

Type.Mixin(String.prototype, Bleroy.Mixin.StringHtmlExtensions);
$("decoded").innerHTML = $("userInput").value.htmlDecode();

This is actually analogous to C# 3.0 extender methods. And this time, no need to box the string: once the type itself has been extended, all instances, existing and future, will get the extensions. This looks kind of cool as a way to package a collection of methods but it would still be more direct to just directly extend the string prototype.

Where mixins really shine is when they're not restricted to a single type like above. Here's another mixin that can be applied on any object:

Bleroy.Mixin.Duck = function() {
    throw new Error("Can't instantiate a mixin.");
}
Bleroy.Mixin.Duck.prototype = {
    quack: function() {
        return this.quackSound;
    },
    walk: function() {
        // implement duck walk here.
    }
}
Bleroy.Mixin.Duck.type = Bleroy.Mixin.IDuck;

This one will not only add the methods to the object you apply it to, it will also make it a valid implementation of the IDuck interface. Here's an example of how you can use it to transform a plain JavaScript object (like something you may have got as a JSON expression from a web service for example) into a legitimate implementation of the IDuck interface:

var myDuck = {quackSound: "qwak!"};
Type.Mixin(myDuck, Bleroy.Mixin.Duck);
// using our new duck
if (Bleroy.Mixin.IDuck.isImplementedBy(myDuck)) {
    alert(myDuck.quack());
}

Like above, you can also apply the same treatment to a type instead of just one object:

Bleroy.Mixin.RubberDucky = function() {
    this.quackSound = "skweek!";
}
Bleroy.Mixin.RubberDucky.registerClass('Bleroy.Mixin.RubberDucky');
Type.Mixin(Bleroy.Mixin.RubberDucky.prototype, Bleroy.Mixin.Duck);

One might argue that this is just a fancy way of doing multiple inheritance and we all know what a mess that is. Well, it is. But it's also a nice way to package a canonical implementation for an interface which is actually coupled with the interface type. I really like that it's still interface-based programming.

Now the thing about all this is that it's not exactly necessary. For example, to associate an interface and its implementation to a plain object, you can just write a class that encapsulates the implementation and that takes a plain object as a constructor parameter.

I'd really love to read what you guys think about that. Would you use something like this? Why would you use it over more traditional inheritance techniques?

The full source code can be downloaded here (you need to extract that into a web site with the latest CTP of Atlas installed):
http://weblogs.asp.net/bleroy/attachment/463965.ashx

UPDATE: The prototype library has something very similar to this, Object.extend (thanks to Zach for pointing that out). You can check it out as well as more useful examples of mixins here: http://www.sitepoint.com/article/painless-javascript-prototype

Comments

Kori Francis said:

Wow .. I think the idea is novel, but the name ... could use some work (imo).
# August 5, 2006 4:11 AM

Bertrand Le Roy said:

Kori: actually no, it's not a new concept, nor is the name. Look it up in wikipedia:

http://en.wikipedia.org/wiki/Mixin

# August 5, 2006 3:08 PM

Travis James said:

This is very cool stuff that was highlighted as an important and powerful language feature at the recent Lang.NET Symposium.
# August 6, 2006 7:12 AM

InfinitiesLoop said:

Even if its not strictly "necessary", just the fact its comparable to extention methods in C# makes it a good fit for Atlas. Atlas has many analogies to .net, why not take extentions methods too? In fact, why not call it an extention method instead of a mixin (even if that is what it is).
# August 6, 2006 2:00 PM

Zach Parrish said:

I would go so far as to say that is strictly "necessary". I'm glad to see someone do it for Atlas coding. I use Prototype quite a bit and it would be pretty useless to me without its Object.extend functionality, which is essentially the same idea.
# August 7, 2006 9:18 AM

David Taylor said:

Yeah...it probably makes sense given many developers will be learning about extension methods in C# anyway. On another topic: It would be great to see Atlas or the control toolkit to include a Tab (TabbedPanel) control allowing you to move between different tabs and have different panels show. The accordian control is great, but a tab control would be more useful for most people. I dont know if you remember...but back in 2002 Microsoft release a DHTML ASP.NET control that did this for IE only, but for all other browsers it fell back to postbacks. David Taylor
# August 7, 2006 12:07 PM

Bertrand Le Roy said:

David: I'll hand that comment over to the toolkit folks.

Zach: I didn't know prototype did that. I'll have a look at their public docs.

# August 7, 2006 2:35 PM

mike said:

yeah, thats one of the key things that keeps me using prototype is the mixins, along with extending the dom element to do quick styling or using css selector to bind to many elements. Being about to do $('id').setStyle({position: 'absolute', zIndex: 99}); is just wicked. however you do have the xml script and the nice intregration with asp.net server control going for the atlas project, those i wish it was somehow more simplified. I'm sure whenever they upgrade Vs to handle javascript intellisence that it will take care of much of that issue. I know its not easy to please everyone and that you have to make it easy for the common developer who knows nothing of javascript, so thanks for extra hard work and time for pushing MS technologies forward. and i can't wait for c# 3.0
# August 10, 2006 6:15 AM

terraslate said:

oh leave it alone. everyones trying to come out with something new. there are more ways to do the same thing coming out than you can shake a stick at. yeah i've been a developer for over a decade, i've seen it all i reckon, and this is just one more of those things that someone has come out with in the hope that we'll all think - wow - how did i ever do my job without it. sure if it makes your life more interesting keep at it - me - i don't need one more thing that nobody needs.
# September 4, 2006 9:32 AM

Bertrand Le Roy said:

Terraslate: I understand what you're saying and that's precisely the reason why we're submitting this to public feedback. I'd like to point out once more though that this is actually nothing new and that this concept is present in quite a few dynamic languages.

# September 4, 2006 5:07 PM