picomath/poortf.js

49 lines
1.7 KiB
JavaScript

/* Totally minimal "typed functions" */
/* helper: dest is really a TF and imps is either nothing, an imp, or
* an array of implementations. author is the entity responsible for these
* implementations.
*/
const addImps = (dest, imps, author) => {
if (imps) {
if (!Array.isArray(imps[0])) imps = [imps]
if (author) {
if (dest.authors.has(author)) {
const [count, index] = dest.authors.get(author)
if (count) dest.imps.splice(index, count)
}
dest.authors.set(author, [imps.length, dest.imps.length])
}
for (const imp of imps) dest.imps.push(imp)
}
}
/* Create a TF, optionally adding some initial imps and further optionally
* registering them to an author. There are two advantages to author
* registration: (1) if the same author adds implementations, the prior imps
* will be deleted, and (2) the author can be invalidated, and the TF will
* lazily call the author back the next time it is called to re-add the imps.
*/
export default function poortf (name, imps, author) {
/* This is the (function) object we will return */
function fn () {
for (const imp of fn.imps) {
if (imp[0](arguments)) return imp[1].apply(null, arguments)
}
throw new TypeError(
`TF ${fn.name}: No match for ${arguments[0]}, ${arguments[1]}, ...`)
}
/* Now dress it up for use */
Object.defineProperty(fn, 'name', {value: name})
fn.imps = []
fn.authors = new Map() // tracks who made each implementation
addImps(fn, imps, author)
fn.addImps = (newI, author) => addImps(fn, newI, author)
return fn
}
export function isTF(x) {
return typeof x === 'function' && 'imps' in x && Array.isArray(x.imps)
}