feat: Add return types for all generic operations
This commit is contained in:
parent
23b3ef4fdd
commit
3957ae8adf
@ -1,4 +1,6 @@
|
|||||||
import PocomathInstance from '../../core/PocomathInstance.mjs'
|
import PocomathInstance from '../../core/PocomathInstance.mjs'
|
||||||
|
import Returns from '../../core/Returns.mjs'
|
||||||
|
|
||||||
/* creates a PocomathInstance incorporating a new numeric type encapsulated
|
/* creates a PocomathInstance incorporating a new numeric type encapsulated
|
||||||
* as a class. (This instance can the be `install()ed` in another to add the
|
* as a class. (This instance can the be `install()ed` in another to add the
|
||||||
* type so created.)
|
* type so created.)
|
||||||
@ -22,15 +24,15 @@ export default function adapted(name, Thing, overrides) {
|
|||||||
// first a creator function, with name depending on the name of the thing:
|
// first a creator function, with name depending on the name of the thing:
|
||||||
const creatorName = overrides.creatorName || name.toLowerCase()
|
const creatorName = overrides.creatorName || name.toLowerCase()
|
||||||
const creator = overrides[creatorName]
|
const creator = overrides[creatorName]
|
||||||
? overrides[creatorName]('')
|
? overrides[creatorName]['']
|
||||||
: Thing[creatorName]
|
: Thing[creatorName]
|
||||||
? (Thing[creatorName])
|
? (Thing[creatorName])
|
||||||
: ((...args) => new Thing(...args))
|
: ((...args) => new Thing(...args))
|
||||||
const defaultCreatorImps = {
|
const defaultCreatorImps = {
|
||||||
'': () => () => creator(),
|
'': () => Returns(name, () => creator()),
|
||||||
'...any': () => args => creator(...args)
|
'...any': () => Returns(name, args => creator(...args))
|
||||||
}
|
}
|
||||||
defaultCreatorImps[name] = () => x => x // x.clone(x)?
|
defaultCreatorImps[name] = () => Returns(name, x => x) // x.clone(x)?
|
||||||
operations[creatorName] = overrides[creatorName] || defaultCreatorImps
|
operations[creatorName] = overrides[creatorName] || defaultCreatorImps
|
||||||
|
|
||||||
// We make the default instance, just as a place to check for methods
|
// We make the default instance, just as a place to check for methods
|
||||||
@ -47,34 +49,35 @@ export default function adapted(name, Thing, overrides) {
|
|||||||
negate: 'neg'
|
negate: 'neg'
|
||||||
}
|
}
|
||||||
const binaryOps = {
|
const binaryOps = {
|
||||||
add: 'add',
|
add: ['add', name],
|
||||||
compare: 'compare',
|
compare: ['compare', name],
|
||||||
divide: 'div',
|
divide: ['div', name],
|
||||||
equalTT: 'equals',
|
equalTT: ['equals', 'boolean'],
|
||||||
gcd: 'gcd',
|
gcd: ['gcd', name],
|
||||||
lcm: 'lcm',
|
lcm: ['lcm', name],
|
||||||
mod: 'mod',
|
mod: ['mod', name],
|
||||||
multiply: 'mul',
|
multiply: ['mul', name],
|
||||||
subtract: 'sub'
|
subtract: ['sub', name]
|
||||||
}
|
}
|
||||||
for (const [mathname, standardname] of Object.entries(unaryOps)) {
|
for (const [mathname, standardname] of Object.entries(unaryOps)) {
|
||||||
if (standardname in instance) {
|
if (standardname in instance) {
|
||||||
operations[mathname] = {}
|
operations[mathname] = {}
|
||||||
operations[mathname][name] = () => t => t[standardname]()
|
operations[mathname][name] = () => Returns(name, t => t[standardname]())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
operations.zero = {}
|
operations.zero = {}
|
||||||
operations.zero[name] = () => t => creator()
|
operations.zero[name] = () => Returns(name, t => creator())
|
||||||
operations.one = {}
|
operations.one = {}
|
||||||
operations.one[name] = () => t => creator(1)
|
operations.one[name] = () => Returns(name, t => creator(1))
|
||||||
operations.conjugate = {}
|
operations.conjugate = {}
|
||||||
operations.conjugate[name] = () => t => t // or t.clone() ??
|
operations.conjugate[name] = () => Returns(name, t => t) // or t.clone() ??
|
||||||
|
|
||||||
const binarySignature = `${name},${name}`
|
const binarySignature = `${name},${name}`
|
||||||
for (const [mathname, standardname] of Object.entries(binaryOps)) {
|
for (const [mathname, spec] of Object.entries(binaryOps)) {
|
||||||
if (standardname in instance) {
|
if (spec[0] in instance) {
|
||||||
operations[mathname] = {}
|
operations[mathname] = {}
|
||||||
operations[mathname][binarySignature] = () => (t,u) => t[standardname](u)
|
operations[mathname][binarySignature] = () => Returns(
|
||||||
|
spec[1], (t,u) => t[spec[0]](u))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ('operations' in overrides) {
|
if ('operations' in overrides) {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import {adapted} from './Types/adapted.mjs'
|
import {adapted} from './Types/adapted.mjs'
|
||||||
import Fraction from 'fraction.js/bigfraction.js'
|
import Fraction from 'fraction.js/bigfraction.js'
|
||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
|
|
||||||
export * from './arithmetic.mjs'
|
export * from './arithmetic.mjs'
|
||||||
export * from './relational.mjs'
|
export * from './relational.mjs'
|
||||||
@ -8,15 +9,18 @@ export const fraction = adapted('Fraction', Fraction, {
|
|||||||
before: ['Complex'],
|
before: ['Complex'],
|
||||||
from: {number: n => new Fraction(n)},
|
from: {number: n => new Fraction(n)},
|
||||||
operations: {
|
operations: {
|
||||||
compare: {'Fraction,Fraction': () => (f,g) => new Fraction(f.compare(g))},
|
compare: {
|
||||||
|
'Fraction,Fraction': () => Returns(
|
||||||
|
'Fraction', (f,g) => new Fraction(f.compare(g)))
|
||||||
|
},
|
||||||
mod: {
|
mod: {
|
||||||
'Fraction,Fraction': () => (n,d) => {
|
'Fraction,Fraction': () => Returns('Fraction', (n,d) => {
|
||||||
// patch for "mathematician's modulus"
|
// patch for "mathematician's modulus"
|
||||||
// OK to use full public API of Fraction here
|
// OK to use full public API of Fraction here
|
||||||
const fmod = n.mod(d)
|
const fmod = n.mod(d)
|
||||||
if (fmod.s === -1n) return fmod.add(d.abs())
|
if (fmod.s === -1n) return fmod.add(d.abs())
|
||||||
return fmod
|
return fmod
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
|
|
||||||
export const divide = {
|
export const divide = {
|
||||||
'T,T': ({
|
'T,T': ({
|
||||||
|
T,
|
||||||
'multiply(T,T)': multT,
|
'multiply(T,T)': multT,
|
||||||
'invert(T)': invT
|
'invert(T)': invT
|
||||||
}) => (x, y) => multT(x, invT(y))
|
}) => Returns(T, (x, y) => multT(x, invT(y)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
import {reducingOperation} from './reducingOperation.mjs'
|
import {reducingOperation} from './reducingOperation.mjs'
|
||||||
|
|
||||||
export const lcm = {
|
export const lcm = {
|
||||||
'T,T': ({
|
'T,T': ({
|
||||||
|
T,
|
||||||
'multiply(T,T)': multT,
|
'multiply(T,T)': multT,
|
||||||
'quotient(T,T)': quotT,
|
'quotient(T,T)': quotT,
|
||||||
'gcd(T,T)': gcdT
|
'gcd(T,T)': gcdT
|
||||||
}) => (a,b) => multT(quotT(a, gcdT(a,b)), b)
|
}) => Returns(T, (a,b) => multT(quotT(a, gcdT(a,b)), b))
|
||||||
}
|
}
|
||||||
Object.assign(lcm, reducingOperation)
|
Object.assign(lcm, reducingOperation)
|
||||||
|
@ -1,3 +1,8 @@
|
|||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
export const mean = {
|
export const mean = {
|
||||||
'...any': ({add, divide}) => args => divide(add(...args), args.length)
|
'...T': ({
|
||||||
|
T,
|
||||||
|
add,
|
||||||
|
'divide(T,NumInt)': div
|
||||||
|
}) => Returns(T, args => div(add(...args), args.length))
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
|
|
||||||
export const mod = {
|
export const mod = {
|
||||||
'T,T': ({
|
'T,T': ({
|
||||||
|
T,
|
||||||
'subtract(T,T)': subT,
|
'subtract(T,T)': subT,
|
||||||
'multiply(T,T)': multT,
|
'multiply(T,T)': multT,
|
||||||
'quotient(T,T)': quotT
|
'quotient(T,T)': quotT
|
||||||
}) => (a,m) => subT(a, multT(m, quotT(a,m)))
|
}) => Returns(T, (a,m) => subT(a, multT(m, quotT(a,m))))
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,9 @@
|
|||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
|
|
||||||
export const quotient = {
|
export const quotient = {
|
||||||
'T,T': ({'floor(T)': flr, 'divide(T,T)':div}) => (n,d) => flr(div(n,d))
|
'T,T': ({
|
||||||
|
T,
|
||||||
|
'floor(T)': flr,
|
||||||
|
'divide(T,T)': div
|
||||||
|
}) => Returns(T, (n,d) => flr(div(n,d)))
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,16 @@
|
|||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
export * from './Types/generic.mjs'
|
export * from './Types/generic.mjs'
|
||||||
|
|
||||||
export const reducingOperation = {
|
export const reducingOperation = {
|
||||||
'undefined': () => u => u,
|
'undefined': () => Returns('undefined', u => u),
|
||||||
'undefined,...any': () => (u, rest) => u,
|
'undefined,...any': () => Returns('undefined', (u, rest) => u),
|
||||||
'any,undefined': () => (x, u) => u,
|
'any,undefined': () => Returns('undefined', (x, u) => u),
|
||||||
'undefined,undefined': () => (u,v) => u,
|
'undefined,undefined': () => Returns('undefined', (u,v) => u),
|
||||||
any: () => x => x,
|
T: ({T}) => Returns(T, x => x),
|
||||||
|
// Unfortunately the type language of Pocomath is not (yet?) expressive
|
||||||
|
// enough to properly type the full reduction signature here:
|
||||||
'any,any,...any': ({
|
'any,any,...any': ({
|
||||||
self
|
self
|
||||||
}) => (a,b,rest) => [b, ...rest].reduce((x,y) => self(x,y), a)
|
}) => Returns('any', (a,b,rest) => [b, ...rest].reduce((x,y) => self(x,y), a))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,34 +1,45 @@
|
|||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
|
|
||||||
export const compare = {
|
export const compare = {
|
||||||
'undefined,undefined': () => () => 0
|
'undefined,undefined': () => Returns('NumInt', () => 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const isZero = {
|
export const isZero = {
|
||||||
'undefined': () => u => u === 0,
|
'undefined': () => Returns('boolean', u => u === 0),
|
||||||
T: ({'equal(T,T)': eq, 'zero(T)': zr}) => t => eq(t, zr(t))
|
T: ({
|
||||||
|
T,
|
||||||
|
'equal(T,T)': eq,
|
||||||
|
'zero(T)': zr
|
||||||
|
}) => Returns('boolean', t => eq(t, zr(t)))
|
||||||
}
|
}
|
||||||
|
|
||||||
export const equal = {
|
export const equal = {
|
||||||
'any,any': ({equalTT, joinTypes, Templates, typeOf}) => (x,y) => {
|
'any,any': ({
|
||||||
|
equalTT,
|
||||||
|
joinTypes,
|
||||||
|
Templates,
|
||||||
|
typeOf
|
||||||
|
}) => Returns('boolean', (x,y) => {
|
||||||
const resultant = joinTypes([typeOf(x), typeOf(y)], 'convert')
|
const resultant = joinTypes([typeOf(x), typeOf(y)], 'convert')
|
||||||
if (resultant === 'any' || resultant in Templates) {
|
if (resultant === 'any' || resultant in Templates) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return equalTT(x,y)
|
return equalTT(x,y)
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export const equalTT = {
|
export const equalTT = {
|
||||||
'T,T': ({
|
'T,T': ({
|
||||||
'compare(T,T)': cmp,
|
'compare(T,T)': cmp,
|
||||||
'isZero(T)': isZ
|
'isZero(T)': isZ
|
||||||
}) => (x,y) => isZ(cmp(x,y)),
|
}) => Returns('boolean', (x,y) => isZ(cmp(x,y)))
|
||||||
// If templates were native to typed-function, we should be able to
|
// If templates were native to typed-function, we should be able to
|
||||||
// do something like:
|
// do something like:
|
||||||
// 'any,any': () => () => false // should only be hit for different types
|
// 'any,any': () => () => false // should only be hit for different types
|
||||||
}
|
}
|
||||||
|
|
||||||
export const unequal = {
|
export const unequal = {
|
||||||
'any,any': ({equal}) => (x,y) => !(equal(x,y))
|
'any,any': ({equal}) => Returns('boolean', (x,y) => !(equal(x,y)))
|
||||||
}
|
}
|
||||||
|
|
||||||
export const larger = {
|
export const larger = {
|
||||||
@ -36,7 +47,7 @@ export const larger = {
|
|||||||
'compare(T,T)': cmp,
|
'compare(T,T)': cmp,
|
||||||
'one(T)' : uno,
|
'one(T)' : uno,
|
||||||
'equalTT(T,T)' : eq
|
'equalTT(T,T)' : eq
|
||||||
}) => (x,y) => eq(cmp(x,y), uno(y))
|
}) => Returns('boolean', (x,y) => eq(cmp(x,y), uno(y)))
|
||||||
}
|
}
|
||||||
|
|
||||||
export const largerEq = {
|
export const largerEq = {
|
||||||
@ -45,10 +56,10 @@ export const largerEq = {
|
|||||||
'one(T)' : uno,
|
'one(T)' : uno,
|
||||||
'isZero(T)' : isZ,
|
'isZero(T)' : isZ,
|
||||||
'equalTT(T,T)': eq
|
'equalTT(T,T)': eq
|
||||||
}) => (x,y) => {
|
}) => Returns('boolean', (x,y) => {
|
||||||
const c = cmp(x,y)
|
const c = cmp(x,y)
|
||||||
return isZ(c) || eq(c, uno(y))
|
return isZ(c) || eq(c, uno(y))
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export const smaller = {
|
export const smaller = {
|
||||||
@ -57,10 +68,10 @@ export const smaller = {
|
|||||||
'one(T)' : uno,
|
'one(T)' : uno,
|
||||||
'isZero(T)' : isZ,
|
'isZero(T)' : isZ,
|
||||||
unequal
|
unequal
|
||||||
}) => (x,y) => {
|
}) => Returns('boolean', (x,y) => {
|
||||||
const c = cmp(x,y)
|
const c = cmp(x,y)
|
||||||
return !isZ(c) && unequal(c, uno(y))
|
return !isZ(c) && unequal(c, uno(y))
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export const smallerEq = {
|
export const smallerEq = {
|
||||||
@ -68,5 +79,5 @@ export const smallerEq = {
|
|||||||
'compare(T,T)': cmp,
|
'compare(T,T)': cmp,
|
||||||
'one(T)' : uno,
|
'one(T)' : uno,
|
||||||
unequal
|
unequal
|
||||||
}) => (x,y) => unequal(cmp(x,y), uno(y))
|
}) => Returns('boolean', (x,y) => unequal(cmp(x,y), uno(y)))
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,9 @@
|
|||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
|
|
||||||
export const roundquotient = {
|
export const roundquotient = {
|
||||||
'T,T': ({'round(T)': rnd, 'divide(T,T)':div}) => (n,d) => rnd(div(n,d))
|
'T,T': ({
|
||||||
|
T,
|
||||||
|
'round(T)': rnd,
|
||||||
|
'divide(T,T)':div
|
||||||
|
}) => Returns(T, (n,d) => rnd(div(n,d)))
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,9 @@
|
|||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
|
|
||||||
export const sign = {
|
export const sign = {
|
||||||
T: ({'compare(T,T)': cmp, 'zero(T)': Z}) => x => cmp(x, Z(x))
|
T: ({
|
||||||
|
T,
|
||||||
|
'compare(T,T)': cmp,
|
||||||
|
'zero(T)': Z
|
||||||
|
}) => Returns(T, x => cmp(x, Z(x)))
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
export * from './Types/generic.mjs'
|
export * from './Types/generic.mjs'
|
||||||
|
|
||||||
export const sqrt = {undefined: () => () => undefined}
|
export const sqrt = {undefined: () => Returns('undefined', () => undefined)}
|
||||||
|
@ -1,3 +1,9 @@
|
|||||||
|
import Returns from '../core/Returns.mjs'
|
||||||
|
|
||||||
export const subtract = {
|
export const subtract = {
|
||||||
'T,T': ({'add(T,T)': addT, 'negate(T)': negT}) => (x,y) => addT(x, negT(y))
|
'T,T': ({
|
||||||
|
T,
|
||||||
|
'add(T,T)': addT,
|
||||||
|
'negate(T)': negT
|
||||||
|
}) => Returns(T, (x,y) => addT(x, negT(y)))
|
||||||
}
|
}
|
||||||
|
@ -34,6 +34,11 @@ describe('The default full pocomath instance "math"', () => {
|
|||||||
math.add(3, math.complex(2.5, 1)), math.complex(5.5, 1))
|
math.add(3, math.complex(2.5, 1)), math.complex(5.5, 1))
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
math.returnTypeOf('add', 'Complex<number>,NumInt'), 'Complex<number>')
|
math.returnTypeOf('add', 'Complex<number>,NumInt'), 'Complex<number>')
|
||||||
|
// The following is not actually what we want, but the Pocomath type
|
||||||
|
// language isn't powerful enough at this point to capture the true
|
||||||
|
// return type:
|
||||||
|
assert.strictEqual(
|
||||||
|
math.returnTypeOf('add', 'number,NumInt,Complex<number>'), 'any')
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
math.returnTypeOf('chain', 'bigint'), 'Chain<bigint>')
|
math.returnTypeOf('chain', 'bigint'), 'Chain<bigint>')
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
|
20
test/generic/_all.mjs
Normal file
20
test/generic/_all.mjs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import assert from 'assert'
|
||||||
|
import math from '../../src/pocomath.mjs'
|
||||||
|
|
||||||
|
describe('generic', () => {
|
||||||
|
it('calculates mean', () => {
|
||||||
|
assert.strictEqual(math.mean(1,2.5,3.25,4.75), 2.875)
|
||||||
|
assert.strictEqual(
|
||||||
|
math.returnTypeOf('mean', 'number,number,number,number'),
|
||||||
|
'number'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
it('compares things', () => {
|
||||||
|
assert.strictEqual(math.larger(7n, 3n), true)
|
||||||
|
assert.strictEqual(
|
||||||
|
math.returnTypeOf('larger', 'bigint,bigint'), 'boolean')
|
||||||
|
assert.strictEqual(math.smallerEq(7.2, 3), false)
|
||||||
|
assert.strictEqual(
|
||||||
|
math.returnTypeOf('smallerEq', 'number,NumInt'), 'boolean')
|
||||||
|
})
|
||||||
|
})
|
@ -92,4 +92,10 @@ describe('fraction', () => {
|
|||||||
assert.deepStrictEqual(math.square(tf), math.fraction(9/16))
|
assert.deepStrictEqual(math.square(tf), math.fraction(9/16))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('knows the types of its operations', () => {
|
||||||
|
assert.deepStrictEqual(
|
||||||
|
math.returnTypeOf('ceiling', 'Fraction'), 'Fraction')
|
||||||
|
assert.deepStrictEqual(
|
||||||
|
math.returnTypeOf('multiply', 'Fraction,Fraction'), 'Fraction')
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
Loading…
Reference in New Issue
Block a user