Skip to content

Instantly share code, notes, and snippets.

@nasser
Created July 4, 2020 20:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nasser/1dfc02fa11768f5df5149adcb973d9b0 to your computer and use it in GitHub Desktop.
Save nasser/1dfc02fa11768f5df5149adcb973d9b0 to your computer and use it in GitHub Desktop.
Generic Functions + Polymorphic Inline Cache Callsites
// multiple dispatch generic functions and polymorphic inline caches
class GenericFunction {
cache: any = {}
addMethod(signature: string[], method) {
let cacheObject = this.cache;
for (const typeName of signature) {
if (!cacheObject.hasOwnProperty(typeName)) {
cacheObject[typeName] = {}
}
cacheObject = cacheObject[typeName];
}
cacheObject._method = method
}
getMethod(signature: string[]) {
let cacheObject = this.cache;
for (const typeName of signature) {
if (!cacheObject.hasOwnProperty(typeName)) {
return null;
}
cacheObject = cacheObject[typeName];
}
return cacheObject._method || null
}
}
function getType(x: any) {
return x.__proto__.constructor.name
}
function generateTypeCheck(signature: string[], idents: string[]) {
let code = []
for (let i = 0; i < signature.length; i++) {
code.push(`(${idents[i]}.__proto__.constructor.name === "${signature[i]}")`)
}
return code.join(" && ")
}
class Callsite {
invoke: any
gf: GenericFunction
constructor(gf: GenericFunction) {
this.gf = gf;
this.invoke = function (...args) {
return this.tryBindInvoke(args)
}
}
tryBindInvoke(args) {
args = args.filter(x=>x) // remove any undefined
let sig = args.map(getType);
let m = this.gf.getMethod(sig)
if (m) {
this.invoke = this.makeNewInvoke(sig, m)
return this.invoke.apply(this, args)
} else {
console.log('no match', sig)
}
}
makeNewInvoke(signature: string[], method) {
let newInvokeSignature = signature.map((_, i) => `arg_${i}`)
let typeCheck = generateTypeCheck(signature, newInvokeSignature)
let code = `(function (arg_method, ${newInvokeSignature.join(',')}, ..._rest) {\n`
+ `if(_rest.length === 0 &&${typeCheck}) { return arg_method(${newInvokeSignature.join(",")}) }\n`
+ `else { return this.tryBindInvoke([${newInvokeSignature.join(',')}, ..._rest]) } })`
let f = eval(code)
let ff = f.bind(this, method)
return ff
}
}
////////////////
function simple(s) {
return "string" + s
}
function simpleAddition(a, b) {
return a + b
}
let g = new GenericFunction();
g.addMethod(["String"],
s => "string: " + s)
g.addMethod(["String", "String"],
(s, t) => "two strings: " + s + t)
g.addMethod(["Number", "Number"],
(a, b) => a + b)
class v3
{
constructor(public x: number, public y: number, public z: number) {}
}
let c = new Callsite(g)
let u = new v3(5, 6, 7)
let v = new v3(8, 10, 20)
g.addMethod(["v3", "v3"], (a:v3, b:v3) =>
new v3(a.x + b.x, a.y + b.y, a.z + b.z))
c.invoke(5, 6) // => 11
c.invoke("Jorm") // => string: Jorm
c.invoke("Hello", "World") // => two strings: HelloWorld
c.invoke(u, v) // => v3 { x: 13, y: 16, z: 27 }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment