feat: Add and illustrate multiple ways of specifying implementations (#19)
Resolves #9. Co-authored-by: Glen Whitney <glen@studioinfinity.org> Reviewed-on: #19
This commit is contained in:
parent
4fdafc751e
commit
d72c443616
9 changed files with 81 additions and 22 deletions
|
@ -1,6 +1,10 @@
|
|||
/* Core of pocomath: create an instance */
|
||||
import typed from 'typed-function'
|
||||
|
||||
export function use(dependencies, implementation) {
|
||||
return [dependencies, implementation]
|
||||
}
|
||||
|
||||
export default class PocomathInstance {
|
||||
/* Disallowed names for ops; beware, this is slightly non-DRY
|
||||
* in that if a new top-level PocomathInstance method is added, its name
|
||||
|
@ -24,15 +28,16 @@ export default class PocomathInstance {
|
|||
* @param {Object<string, Object<Signature, [string[], function]>>} ops
|
||||
* The only parameter ops gives the semantics of the operations to install.
|
||||
* The keys are operation names. The value for a key is an object
|
||||
* mapping (typed-function) signature strings to pairs of dependency
|
||||
* lists and implementation functions.
|
||||
* mapping (typed-function) signature strings to specifications of
|
||||
* of dependency lists and implementation functions.
|
||||
*
|
||||
* A dependency list is a list of strings. Each string can either be the
|
||||
* name of a function that the corresponding implementation has to call,
|
||||
* or a specification of a particular signature of a function that it has
|
||||
* to call, in the form 'FN(SIGNATURE)'. Note the function name can be
|
||||
* the special value 'self' to indicate a recursive call to the given
|
||||
* operation (either with or without a particular signature.
|
||||
* to call, in the form 'FN(SIGNATURE)' [not implemented yet].
|
||||
* Note the function name can be the special value 'self' to indicate a
|
||||
* recursive call to the given operation (either with or without a
|
||||
* particular signature.
|
||||
*
|
||||
* There are two cases for the implementation function. If the dependency
|
||||
* list is empty, it should be a function taking the arguments specified
|
||||
|
@ -41,6 +46,30 @@ export default class PocomathInstance {
|
|||
* requested functions as values, to a function taking the arguments
|
||||
* specified by the signature and returning the value.
|
||||
*
|
||||
* There are various specifications currently allowed for the
|
||||
* dependency list and implementation function:
|
||||
*
|
||||
* 1) Just a function. Then the dependency list is assumed to be empty.
|
||||
*
|
||||
* 2) A pair (= Array with two entries) of a dependency list and the
|
||||
* implementation function.
|
||||
*
|
||||
* 3) An object whose property named 'does' gives the implementation
|
||||
* function and whose property named 'uses', if present, gives the
|
||||
* dependency list (which is assumed to be empty if the property is
|
||||
* not present).
|
||||
*
|
||||
* 4) A call to the 'use' function exported from the this module, with
|
||||
* first argument the dependencies and second argument the
|
||||
* implementation.
|
||||
*
|
||||
* For a visual comparison of the options, this proof-of-concept uses
|
||||
* option (1) when possible for the 'number' type, (3) for the 'Complex'
|
||||
* type, (4) for the 'bigint' type, and (2) under any other circumstances.
|
||||
* Likely a fleshed-out version of this scheme would settle on just one
|
||||
* or two of these options or variants thereof, rather than providing so
|
||||
* many different ones.
|
||||
*
|
||||
* Note that the "operation" named `Types` is special: it gives
|
||||
* types that must be installed in the instance. In this case, the keys
|
||||
* are type names, and the values are objects with a property 'test'
|
||||
|
@ -66,7 +95,12 @@ export default class PocomathInstance {
|
|||
for (const func in this._imps) {
|
||||
if (func === 'Types') continue
|
||||
for (const definition of Object.values(this._imps[func])) {
|
||||
for (const dependency of definition[0]) {
|
||||
let deps = []
|
||||
if (Array.isArray(definition)) deps = definition[0]
|
||||
else if (typeof definition === 'object') {
|
||||
deps = definition.uses || deps
|
||||
}
|
||||
for (const dependency of deps) {
|
||||
const depName = dependency.split('(',1)[0]
|
||||
if (doneSet.has(depName)) continue
|
||||
requiredSet.add(depName)
|
||||
|
@ -154,7 +188,20 @@ export default class PocomathInstance {
|
|||
this._ensureTypes()
|
||||
const tf_imps = {}
|
||||
for (const signature in imps) {
|
||||
const [deps, imp] = imps[signature]
|
||||
const specifier = imps[signature]
|
||||
let deps = []
|
||||
let imp
|
||||
if (typeof specifier === 'function') {
|
||||
imp = specifier
|
||||
} else if (Array.isArray(specifier)) {
|
||||
[deps, imp] = specifier
|
||||
} else if (typeof specifier === 'object') {
|
||||
deps = specifier.uses || deps
|
||||
imp = specifier.does
|
||||
} else {
|
||||
throw new SyntaxError(
|
||||
`Cannot interpret signature definition ${specifier}`)
|
||||
}
|
||||
if (deps.length === 0) {
|
||||
tf_imps[signature] = imp
|
||||
} else {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue