Created
July 4, 2020 20:18
-
-
Save nasser/1dfc02fa11768f5df5149adcb973d9b0 to your computer and use it in GitHub Desktop.
Generic Functions + Polymorphic Inline Cache Callsites
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
// 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