Ad-hoc method extension

JavaScript performance comparison

Test case created by Brian

Info

CEnvironment allows extension by overloading on the input constructor:

var env1 = adhoc.CEnvironment()
    .method('copoint', adhoc.Id, adhoc.idCopoint)
    .method('copoint', adhoc.NonEmptyArray, adhoc.nonEmptyArrayCopoint);

MEnvironment allows extension by overloading with a predicate:

var env2 = adhoc.MEnvironment()
    .method('copoint', adhoc.isInstanceOf(adhoc.Id), adhoc.idCopoint)
    .method('copoint', adhoc.isInstanceOf(adhoc.NonEmptyArray), adhoc.nonEmptyArrayCopoint);

The predicate is obviously more flexible but is likely to be extremely slow. The constructor test is possibly O(1) if the environment has Harmony Map support.

Preparation code

 
<script>
Benchmark.prototype.setup = function() {
    var adhoc = (function(global) {
        "use strict";
   
        var Map = global.Map || polyfillMap();
   
        function polyfillMap() {
            function Map() {
                var self = getInstance(this, Map);
                self.keys = [];
                self.values = [];
                return self;
            };
   
            // O(n)
            // Would be O(1) when native (i.e. not a polyfill)
            Map.prototype.get = function(k) {
                return this.values[this.keys.indexOf(k)];
            };
   
            Map.prototype.set = function(k, v) {
                this.keys.push(k);
                this.values.push(v);
            };
   
            return Map;
        }
   
        function create(proto) {
            function Ctor() {}
            Ctor.prototype = proto;
            return new Ctor();
        }
       
        function getInstance(self, constructor) {
            return self instanceof constructor ? self : create(constructor.prototype);
        }
   
        function isInstanceOf(c) {
            return function(o) {
                return o instanceof c;
            };
        }
   
        function Id(value) {
            var self = getInstance(this, Id);
            self.value = value;
            return self;
        }
   
        function idCopoint(id) {
            return id.value;
        }
   
        function NonEmptyArray(values) {
            var self = getInstance(this, NonEmptyArray);
            if(!values.length) throw new TypeError("NonEmptyArray got empty array");
            self.values = values;
            return self;
        }
   
        function nonEmptyArrayCopoint(nea) {
            return nea.values[0];
        }
   
        function CEnvironment(methods) {
            var self = getInstance(this, CEnvironment),
                key;
   
            function makeMethod(key) {
                return function(x) {
                    return methods[key].get(x.constructor).apply(this, arguments);
                };
            }
   
            methods = methods || {};
            self.methods = methods;
            for(key in methods) {
                if(self[key]) throw new TypeError(key + ' already exists!');
                self[key] = makeMethod(key);
            }
   
            return self;
        }
        CEnvironment.prototype.method = function(name, constructor, implementation) {
            var methods = {},
                key;
   
            for(key in this.methods) {
                methods[key] = this.methods[key];
            }
   
            if(!methods[name]) methods[name] = new Map();
            methods[name].set(constructor, implementation);
   
            return CEnvironment(methods);
        };
   
        function MEnvironment(methods) {
            var self = getInstance(this, MEnvironment),
                key;
   
            function makeMethod(key) {
                return function() {
                    var i;
                    for(i = 0; i < methods[key].length; i++) {
                        if(!methods[key][i].predicate.apply(this, arguments)) continue;
                        return methods[key][i].implementation.apply(this, arguments);
                    }
                    throw new TypeError(key + " isn't implemented for this input");
                };
            }
   
            methods = methods || {};
            self.methods = methods;
            for(key in methods) {
                if(self[key]) throw new TypeError(key + ' already exists!');
                self[key] = makeMethod(key);
            }
   
            return self;
        }
        MEnvironment.prototype.method = function(name, predicate, implementation) {
            var methods = {},
                key;
   
            for(key in this.methods) {
                methods[key] = this.methods[key];
            }
   
            if(!methods[name]) methods[name] = [];
   
            methods[name].unshift({
                predicate: predicate,
                implementation: implementation
            });
   
            return MEnvironment(methods);
        };
   
        return {
            isInstanceOf: isInstanceOf,
            Id: Id,
            idCopoint: idCopoint,
            NonEmptyArray: NonEmptyArray,
            nonEmptyArrayCopoint: nonEmptyArrayCopoint,
            CEnvironment: CEnvironment,
            MEnvironment: MEnvironment
        };
    })(typeof window == 'undefined' ? global : window);
   
    var env1 = adhoc.CEnvironment()
        .method('copoint', adhoc.Id, adhoc.idCopoint)
        .method('copoint', adhoc.NonEmptyArray, adhoc.nonEmptyArrayCopoint);
   
    var env2 = adhoc.MEnvironment()
        .method('copoint', adhoc.isInstanceOf(adhoc.Id), adhoc.idCopoint)
        .method('copoint', adhoc.isInstanceOf(adhoc.NonEmptyArray), adhoc.nonEmptyArrayCopoint);
};
</script>

Test runner

Warning! For accurate results, please disable Firebug before running the tests. (Why?)

Java applet disabled.

Testing in unknown unknown
Test Ops/sec
Constructor check
env1.copoint(adhoc.Id(42));
env1.copoint(adhoc.NonEmptyArray([142, 152, 162]));
pending…
Multimethods
env2.copoint(adhoc.Id(42));
env2.copoint(adhoc.NonEmptyArray([142, 152, 162]));
pending…
Monomorphic
adhoc.idCopoint(adhoc.Id(42));
adhoc.nonEmptyArrayCopoint(adhoc.NonEmptyArray([142, 152, 162]));
pending…

Compare results of other browsers

Revisions

You can edit these tests or add even more tests to this page by appending /edit to the URL. Here’s a list of current revisions for this page:

0 comments

Add a comment