Last active
December 19, 2015 02:19
-
-
Save zachallaun/5881994 to your computer and use it in GitHub Desktop.
Kinda Clojure-like protocols
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
// Possibly wacky and useless protocol experiment | |
var mapObj = function(obj, fn) { | |
var ret = {} | |
for (var k in obj) { | |
var temp = fn(k, obj[k]) | |
ret[temp[0]] = temp[1] | |
} | |
return ret | |
} | |
// var foo = function(x, y, z) {} | |
// paramNamesOf(foo) | |
// => ['x', 'y', 'z'] | |
// | |
var paramNamesOf = function(fn) { | |
var fnStr = fn.toString() | |
return fnStr.slice(fnStr.indexOf('(') + 1, fnStr.indexOf(')')). | |
match(/([^\s,]+)/g) || [] | |
} | |
var getImpl = function(ctor, protocol) { | |
return ctor._protocolImpls[protocol._protocolId] | |
} | |
var implDefined = function(ctor, protocol) { | |
return ctor._protocols & protocol._protocolId | |
} | |
var extend = function(target, obj) { | |
for (var k in obj) { target[k] = obj[k] } | |
return target | |
} | |
var dispatchFns = function(protocol, declarations) { | |
return mapObj(declarations, function(fnName, sig) { | |
return [fnName, function(dispatchArg /*, args... */) { | |
if (!implDefined(dispatchArg.constructor, protocol)) { | |
throw new Error("Protocol not defined for " + dispatchArg) | |
} | |
return getImpl(dispatchArg.constructor, protocol)[fnName]. | |
apply(dispatchArg, arguments) | |
}] | |
}) | |
} | |
var Protocol = (function() { | |
var _protocolIdCounter = 0 | |
return function Protocol(declarations) { | |
this._protocolId = Math.pow(2, _protocolIdCounter) | |
_protocolIdCounter += 1 | |
this._declarations = declarations | |
extend(this, dispatchFns(this, declarations)) | |
} | |
})() | |
var verifySignatures = function(protocol, impls) { | |
for (var k in protocol._declarations) { | |
var protocolSig = protocol._declarations[k], | |
sigStr = k + "(" + protocolSig.toString() + ")" | |
if (!impls[k]) { | |
throw new Error("Invalid protocol specification: " + | |
"missing implementation for " + sigStr) | |
} | |
var implSig = paramNamesOf(impls[k]) | |
if (impls[k].length !== protocol._declarations[k].length) { | |
throw new Error("Invalid protocol implementation signature for " + | |
sigStr + ": " + k + "(" + implSig.toString() + ")") | |
} | |
} | |
} | |
var extendToProtocol = function(protocol, ctor, impls) { | |
verifySignatures(protocol, impls) | |
ctor._protocols = ctor._protocols | protocol._protocolId | |
ctor._protocolImpls = [] | |
ctor._protocolImpls[protocol._protocolId] = impls | |
} | |
// | |
// Example: | |
// | |
var Set = new Protocol({ | |
isEmpty: ['set'], | |
contains: ['set', 'element'], | |
insert: ['set', 'element'], | |
union: ['set1', 'set2'] | |
}) | |
var SetUnion = function(s1, s2) { | |
this.s1 = s1 | |
this.s2 = s2 | |
} | |
extendToProtocol(Set, SetUnion, { | |
isEmpty: function(self) { | |
return Set.isEmpty(self.s1) && Set.isEmpty(self.s2) | |
}, | |
contains: function(self, element) { | |
return Set.contains(self.s1, element) || Set.contains(self.s2, element) | |
}, | |
insert: function(self, element) { | |
return Set.contains(self, element) ? | |
self : | |
new SetUnion(self.s1, Set.insert(self.s2, element)) | |
}, | |
union: function(self, other) { | |
return new SetUnion(self, other) | |
} | |
}) | |
extendToProtocol(Set, Array, { | |
isEmpty: function(self) { | |
return self.length === 0 | |
}, | |
contains: function(self, element) { | |
return self.indexOf(element) !== -1 | |
}, | |
insert: function(self, element) { | |
return Set.contains(self, element) ? self : self.concat([element]) | |
}, | |
union: function(self, other) { | |
return new SetUnion(self, other) | |
} | |
}) | |
extendToProtocol(Set, Function, { | |
isEmpty: function(self) { | |
return false // uh oh. interface break-down. | |
}, | |
contains: function(self, element) { | |
return self(element) | |
}, | |
insert: function(self, element) { | |
return function(el) { return el === element || self(el) } | |
}, | |
union: function(self, other) { | |
return new SetUnion(self, other) | |
} | |
}) | |
FullSet = true | |
EmptySet = false | |
extendToProtocol(Set, Boolean, { | |
isEmpty: function(self) { return !self }, | |
contains: function(self, el) { return self }, | |
insert: function(self, el) { return new SetUnion(self, [el]) }, | |
union: function(self, other) { return new SetUnion(self, other) } | |
}) | |
Set.contains(Set.union(Set.insert(function() { return false }, 1), | |
Set.insert([1], 2)), | |
2) // => true |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment