Created
February 20, 2013 17:23
-
-
Save clausreinke/4997274 to your computer and use it in GitHub Desktop.
JavaScript: generic methods, programming to interfaces, classes implementing interfaces;
a sketch (load in browser or run with nodejs)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// see console output <script> | |
// quick hack of classes implementing interfaces, just to give the idea; for a | |
// proper design, feel free to use better syntax, (Weak)Maps, unique symbols, ..; | |
// | |
// the core idea is to relate interfaces to classes that implement them, in | |
// such a way that the relevant implementations of interface methods can be | |
// extracted systematically in generic code | |
var Mappable = {}; // interface, to be implemented by classes; | |
// for simplicity, we index by class name: | |
// Mappable.<class>.map(<target>,<function>) | |
// Array implements Mappable | |
Mappable.Array = { map : function(a,f) { | |
return Array.prototype.map.call(a,f) | |
} | |
}; | |
// String implements Mappable | |
Mappable.String = { map : function(s,f) { | |
return Array.prototype.map.call(s,f).join("") | |
} | |
}; | |
// typed arrays implement Mappable, Int32Array as example | |
Mappable.Int32Array = { map : function(i32a,f) { | |
var l = i32a.length; | |
var buf = new ArrayBuffer(l*4); | |
var res = new Int32Array(buf); | |
for (var i=0; i<i32a.length; i++) | |
res[i] = f(i32a[i]); | |
return res; | |
} | |
}; | |
// copy and modify for the other typed arrays.. highly inelegant; | |
// | |
// better would be to separate Array<>-specific loop and <element>-specific | |
// size/conversion/whatever; | |
// | |
// to get there, we'd need to turn Int32Array into Array<Int32> and have | |
// support for letting Array<elemtype> implement Mappable, with code access | |
// to interfaces implemented by either Array<> or elemtype | |
// a map for all seasons; generic, extensible | |
function genericMap(x,xElemMapper) { | |
var xClassName = x.constructor.name; // console.log("// "+xClassName); | |
return Mappable[xClassName].map(x,xElemMapper) ; | |
} | |
// a curried map, swapped arguments for easier composition | |
function curriedMap(xElemMapper) { return function(x) { | |
return genericMap(x,xElemMapper) | |
}} | |
// TODO: iterable should implement Mappable, so from can be separate from map | |
// handle Dictionary (no .constructor) | |
// error handling in genericMap | |
// ------ testing | |
// some container data and element functions | |
var arr_num = [0,1,2,3]; | |
var arr_arr_num = [[1,2,3],[3,2,1]]; | |
var arr_arr_str = [["hi","there"],["gdkkn","vnqkc"]]; | |
var str = "hello"; | |
var i32arr = new Int32Array([2,3,4]); | |
var numf1 = function(x) { return x+1 }; | |
var numf2 = function(x) { return x/2 }; | |
var charf = function(c) { return String.fromCharCode(c.charCodeAt(0)+1) }; | |
console.log("\n// mapping over an array of numbers, explicit class"); | |
console.log( Mappable.Array.map(arr_num,numf1) ); | |
console.log("\n// mapping over a typed array of numbers, explicit class"); | |
console.log( Mappable.Int32Array.map(i32arr,numf2) ); | |
console.log("\n// Int32Array map differs from Array map"); | |
console.log( Mappable.Array.map(i32arr,numf2) ); | |
console.log("\n// mapping over a String, explicit classes;"); | |
console.log( Mappable.String.map(str,charf) ); | |
console.log("\n// String map differs from Array map"); | |
console.log( Mappable.Array.map(str,charf) ); | |
console.log("\n-----------------"); | |
console.log("\n// mapping over array, typed array and string, generically"); | |
console.log( genericMap(arr_num,numf1) ); | |
console.log( genericMap(i32arr,numf1) ); | |
console.log( genericMap(str,charf) ); | |
console.log("\n-----------------"); | |
console.log("\n// deep, nested mapping in nested containers, generically"); | |
console.log( curriedMap(curriedMap(numf1))(arr_arr_num) ); | |
console.log( curriedMap(curriedMap(curriedMap(charf)))(arr_arr_str) ); | |
// ------ another mapping example, nodejs-specific code, needs Q, optional; | |
// demonstrates that map works for non-containers, as well | |
if (typeof require!="undefined") { | |
console.log("\n-----------------"); | |
var Q = require("q"); | |
// Q promise implements Mappable | |
Mappable.makePromise = { map : function(q,f) { | |
var mkQ = function(val) { | |
return Q.fcall(function(){return f(val) }) | |
}; | |
return q.then( mkQ ) | |
} | |
}; | |
var filePromise = Q.nfcall(require("fs").readFile,__filename,"utf-8"); | |
var head = function(text) { return text.split("\n").slice(1,8).join("\n") }; | |
console.log("\n// generically mapping over a promise"); | |
genericMap( filePromise, head ).then(console.log); | |
/* */ | |
} | |
// </script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment