/* 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) }