View RSS Feed

Twey

Generics in Javascript

Rating: 14 votes, 3.64 average.
I just used this code to vent some frustration earlier, hope you all like it. It's pretty self-explanatory. It's probably too theoretical to go into the DD script archives, so I'll just leave it lying about here.

Code:
var Generic = (function() {
  var MATCH_FAIL = {},
      MATCH_ANY  = function() { return MATCH_ANY; };

  function create(fallback) {
    var s = function() {
      for (var i = 0, args = Array.prototype.slice.call(arguments), r; i < specs.length; ++i)
        if ((r = specs[i].match(args)) !== MATCH_FAIL)
          return r;

      if (s._fallback)
        return s._fallback.apply(this, args);

      throw new Error("Generic: No methods matched and no fallback provided.");
    }, specs = s._specs = [];

    s.specialise = specialise;
    s.addFallback = addFallback;
    if (fallback)
      s.addFallback(fallback);

    return s;
  }

  function specialise(patterns, func) {
    var s = this._specs;

    s[s.length] = new Specialisation(patterns, func, this);

    return this;
  }

  function addFallback(func) {
    this._fallback = func;

    return this;
  }

  /**** Begin Specialisation ****/

  function Specialisation(patterns, func, context) {
    this.patterns = patterns;
    this.func = func;
    this.context = context;
  }

  Specialisation.compatible = function(value, pattern) {
    if (pattern === MATCH_ANY && value !== undefined)
      return true;
    else if ((typeof pattern === "string" || pattern instanceof String) && typeof value === pattern)
      return true;
    else if (typeof pattern === "function" && value instanceof pattern)
      return true;
    else if (pattern instanceof Pattern)
      return pattern.guard(value);
    else if (pattern instanceof Interface)
      return pattern.match(value);

    return false;
  };

  Specialisation.prototype = {
    match: function(args) {
      for (var i = 0, a = this.patterns, n = a.length; i < n; ++i)
        if (!Specialisation.compatible(args[i], a[i]))
          return MATCH_FAIL;

      return this.func.apply(this.context, args);
    }
  };

  /**** Begin Pattern ****/

  function Pattern(guard) {
    this.guard = guard;
  }

  function GUARD(func) {
    return new Pattern(func);
  }

  function GUARD_IS(right) {
    return new Pattern(function(val) {
      return val === right;
    });
  }

  /**** Begin Interface ****/

  function Interface(obj) {
    if (!(this instanceof Interface))
      return new Interface(obj);

    for (var x in obj)
      if (obj.hasOwnProperty(x))
        this[x] = obj[x];
  }

  Interface.getSkeleton = function(obj) {
    var r = {};

    for (var x in obj)
      if (obj.hasOwnProperty(x))
        r[x] = typeof obj[x];

    return new Interface(r);
  };

  Interface.prototype = {
    match: function(value) {
      for (var x in this)
        if (this.hasOwnProperty(x) && !Specialisation.compatible(value[x], this[x]))
          return false;

      return true;
    }
  };

  return {
    create: create,
    Interface: Interface,
    GUARD: GUARD,
    GUARD_IS: GUARD_IS,
    MATCH_ANY: MATCH_ANY
  };
})();
Some sample code:

Code:
function Animal(species) {
  this.species = species;
}

var iAnimal = Generic.Interface.getSkeleton(new Animal("foo"));

var iDog = Generic.Interface({'species' : Generic.GUARD_IS("dog")});

var sound = Generic.create()
  .specialise([iDog, 'undefined'], function() {
    print("I got a dog!  I got one!");
  })
  .specialise(['string', iAnimal], function(snd, an) {
    print("A " + an.species + " says '" + snd + "',");
  })
  .specialise([iAnimal, 'string'], function(an, snd) {
    print("but a " + an.species + " says '" + snd + "',");
  })
  .specialise([Animal], function(an) {
    print("and a " + an.species + " is apparently silent.");
  })
  .specialise([Generic.MATCH_ANY, 'undefined'], function(val) {
    print("... and whatever noise a " + val + " makes...");
  })
  .addFallback(function() {
    print("Er... random animal noise?");
  });

sound(new Animal("dog"));
sound("woof", new Animal("dog"));
sound(new Animal("cat"), "miaow");
sound(new Animal("frog"));
sound(42);
sound(4, 5);

Submit "Generics in Javascript" to del.icio.us Submit "Generics in Javascript" to StumbleUpon Submit "Generics in Javascript" to Google Submit "Generics in Javascript" to Digg

Tags: None Add / Edit Tags
Categories
JavaScript & Ajax , Post a JavaScript

Comments