Skip to content

Instantly share code, notes, and snippets.

@zachallaun
Last active December 19, 2015 02:19
Show Gist options
  • Save zachallaun/5881994 to your computer and use it in GitHub Desktop.
Save zachallaun/5881994 to your computer and use it in GitHub Desktop.
Kinda Clojure-like protocols
// 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