feat: First operational instance with just add

This commit is contained in:
Glen Whitney 2022-07-18 17:08:49 -07:00
parent cb016474ec
commit c5384e0ee7
6 changed files with 123 additions and 0 deletions

90
PocomathInstance.mjs Normal file
View File

@ -0,0 +1,90 @@
/* Core of pocomath: create an instance */
import typed from 'typed-function'
export default class PocomathInstance {
constructor(name) {
this.name = name
this._imps = {}
}
/**
* (Partially) define one or more operations of the instance:
*
* @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.
*
* 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.
*
* There are two cases for the implementation function. If the dependency
* list is empty, it should be a function taking the arguments specified
* by the signature and returning the value. Otherwise, it should be
* a function taking an object with the dependency lists as keys and the
* requested functions as values, to a function taking the arguments
* specified by the signature and returning the value
*/
install(ops) {
for (const key in ops) this._installOp(key, ops[key])
}
/* Used internally by install, see the documentation there */
_installOp(name, implementations) {
// new implementations, so set the op up to lazily recreate itself
this._invalidate(name)
const opImps = this._imps[name]
for (const signature in implementations) {
if (signature in opImps) {
if (implemenatations[signature] === opImps[signature]) continue
throw new SyntaxError(
`Conflicting definitions of ${signature} for ${name}`)
} else {
opImps[signature] = implementations[signature]
}
}
}
/**
* Reset an operation to require creation of typed-function,
* and if it has no implementations so far, set them up.
*/
_invalidate(name) {
const self = this
this[name] = function () {
return self._bundle(name).apply(self, arguments)
}
if (!(name in this._imps)) {
this._imps[name] = {}
}
}
/**
* Create a typed-function from the signatures for the given name and
* assign it to the property with that name, returning it as well
*/
_bundle(name) {
const imps = this._imps[name]
if (!imps || Object.keys(imps).length === 0) {
throw new SyntaxError(`No implementations for ${name}`)
}
const tf_imps = {}
// TODO: handle nonempty dependencies
for (const signature in imps) {
const [deps, imp] = imps[signature]
if (deps.length === 0) {
tf_imps[signature] = imp
} else {
throw new Error('Unimplemented')
}
}
const tf = typed(name, tf_imps)
this[name] = tf
return tf
}
}

3
number/add.mjs Normal file
View File

@ -0,0 +1,3 @@
export const add = {
'...number': [[], addends => addends.reduce((x,y) => x+y, 0)],
}

View File

@ -21,4 +21,7 @@
devDependencies: { devDependencies: {
mocha: '^10.0.0', mocha: '^10.0.0',
}, },
dependencies: {
'typed-function': '^3.0.0',
},
} }

View File

@ -2,6 +2,10 @@ lockfileVersion: 5.4
specifiers: specifiers:
mocha: ^10.0.0 mocha: ^10.0.0
typed-function: ^3.0.0
dependencies:
typed-function: 3.0.0
devDependencies: devDependencies:
mocha: 10.0.0 mocha: 10.0.0
@ -465,6 +469,11 @@ packages:
is-number: 7.0.0 is-number: 7.0.0
dev: true dev: true
/typed-function/3.0.0:
resolution: {integrity: sha512-mKJKkt2xYxJUuMD7jyfgUxfn5KCsCxkEKBVjep5yYellJJ5aEDO2QUAmIGdvcZmfQnIrplkzELIaG+5b1475qg==}
engines: {node: '>= 14'}
dev: false
/workerpool/6.2.1: /workerpool/6.2.1:
resolution: {integrity: sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==} resolution: {integrity: sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==}
dev: true dev: true

8
pocomath.mjs Normal file
View File

@ -0,0 +1,8 @@
/* Core of pocomath: generates the default instance */
import PocomathInstance from './PocomathInstance.mjs'
import * as numberAdd from './number/add.mjs'
const math = new PocomathInstance('math')
math.install(numberAdd)
export default math

10
test/_pocomath.mjs Normal file
View File

@ -0,0 +1,10 @@
import assert from 'assert'
import math from '../pocomath.mjs'
describe('The default full pocomath instance "math"', () => {
it('can add numbers', () => {
assert.strictEqual(math.add(3, 4), 7)
assert.strictEqual(math.add(1.5, 2.5, 3.5), 7.5)
assert.strictEqual(math.add(Infinity), Infinity)
})
})