diff --git a/src/generic/Types/adapted.mjs b/src/generic/Types/adapted.mjs index ba20889..6c54fc5 100644 --- a/src/generic/Types/adapted.mjs +++ b/src/generic/Types/adapted.mjs @@ -1,4 +1,6 @@ import PocomathInstance from '../../core/PocomathInstance.mjs' +import Returns from '../../core/Returns.mjs' + /* creates a PocomathInstance incorporating a new numeric type encapsulated * as a class. (This instance can the be `install()ed` in another to add the * 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: const creatorName = overrides.creatorName || name.toLowerCase() const creator = overrides[creatorName] - ? overrides[creatorName]('') + ? overrides[creatorName][''] : Thing[creatorName] ? (Thing[creatorName]) : ((...args) => new Thing(...args)) const defaultCreatorImps = { - '': () => () => creator(), - '...any': () => args => creator(...args) + '': () => Returns(name, () => creator()), + '...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 // 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' } const binaryOps = { - add: 'add', - compare: 'compare', - divide: 'div', - equalTT: 'equals', - gcd: 'gcd', - lcm: 'lcm', - mod: 'mod', - multiply: 'mul', - subtract: 'sub' + add: ['add', name], + compare: ['compare', name], + divide: ['div', name], + equalTT: ['equals', 'boolean'], + gcd: ['gcd', name], + lcm: ['lcm', name], + mod: ['mod', name], + multiply: ['mul', name], + subtract: ['sub', name] } for (const [mathname, standardname] of Object.entries(unaryOps)) { if (standardname in instance) { operations[mathname] = {} - operations[mathname][name] = () => t => t[standardname]() + operations[mathname][name] = () => Returns(name, t => t[standardname]()) } } operations.zero = {} - operations.zero[name] = () => t => creator() + operations.zero[name] = () => Returns(name, t => creator()) operations.one = {} - operations.one[name] = () => t => creator(1) + operations.one[name] = () => Returns(name, t => creator(1)) operations.conjugate = {} - operations.conjugate[name] = () => t => t // or t.clone() ?? + operations.conjugate[name] = () => Returns(name, t => t) // or t.clone() ?? const binarySignature = `${name},${name}` - for (const [mathname, standardname] of Object.entries(binaryOps)) { - if (standardname in instance) { + for (const [mathname, spec] of Object.entries(binaryOps)) { + if (spec[0] in instance) { 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) { diff --git a/src/generic/all.mjs b/src/generic/all.mjs index 7132944..45bd9d0 100644 --- a/src/generic/all.mjs +++ b/src/generic/all.mjs @@ -1,5 +1,6 @@ import {adapted} from './Types/adapted.mjs' import Fraction from 'fraction.js/bigfraction.js' +import Returns from '../core/Returns.mjs' export * from './arithmetic.mjs' export * from './relational.mjs' @@ -8,15 +9,18 @@ export const fraction = adapted('Fraction', Fraction, { before: ['Complex'], from: {number: n => new Fraction(n)}, operations: { - compare: {'Fraction,Fraction': () => (f,g) => new Fraction(f.compare(g))}, + compare: { + 'Fraction,Fraction': () => Returns( + 'Fraction', (f,g) => new Fraction(f.compare(g))) + }, mod: { - 'Fraction,Fraction': () => (n,d) => { + 'Fraction,Fraction': () => Returns('Fraction', (n,d) => { // patch for "mathematician's modulus" // OK to use full public API of Fraction here const fmod = n.mod(d) if (fmod.s === -1n) return fmod.add(d.abs()) return fmod - } + }) } } }) diff --git a/src/generic/divide.mjs b/src/generic/divide.mjs index 1aee89b..ab1e893 100644 --- a/src/generic/divide.mjs +++ b/src/generic/divide.mjs @@ -1,7 +1,10 @@ +import Returns from '../core/Returns.mjs' + export const divide = { 'T,T': ({ + T, 'multiply(T,T)': multT, 'invert(T)': invT - }) => (x, y) => multT(x, invT(y)) + }) => Returns(T, (x, y) => multT(x, invT(y))) } diff --git a/src/generic/lcm.mjs b/src/generic/lcm.mjs index 04e78b5..26bfbf8 100644 --- a/src/generic/lcm.mjs +++ b/src/generic/lcm.mjs @@ -1,10 +1,12 @@ +import Returns from '../core/Returns.mjs' import {reducingOperation} from './reducingOperation.mjs' export const lcm = { 'T,T': ({ + T, 'multiply(T,T)': multT, 'quotient(T,T)': quotT, '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) diff --git a/src/generic/mean.mjs b/src/generic/mean.mjs index d12c21b..58cbc19 100644 --- a/src/generic/mean.mjs +++ b/src/generic/mean.mjs @@ -1,3 +1,8 @@ +import Returns from '../core/Returns.mjs' 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)) } diff --git a/src/generic/mod.mjs b/src/generic/mod.mjs index 84af4e6..e1b5ec6 100644 --- a/src/generic/mod.mjs +++ b/src/generic/mod.mjs @@ -1,7 +1,10 @@ +import Returns from '../core/Returns.mjs' + export const mod = { 'T,T': ({ + T, 'subtract(T,T)': subT, 'multiply(T,T)': multT, 'quotient(T,T)': quotT - }) => (a,m) => subT(a, multT(m, quotT(a,m))) + }) => Returns(T, (a,m) => subT(a, multT(m, quotT(a,m)))) } diff --git a/src/generic/quotient.mjs b/src/generic/quotient.mjs index 54e000a..521bd2a 100644 --- a/src/generic/quotient.mjs +++ b/src/generic/quotient.mjs @@ -1,3 +1,9 @@ +import Returns from '../core/Returns.mjs' + 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))) } diff --git a/src/generic/reducingOperation.mjs b/src/generic/reducingOperation.mjs index e29baf1..3c256f2 100644 --- a/src/generic/reducingOperation.mjs +++ b/src/generic/reducingOperation.mjs @@ -1,13 +1,16 @@ +import Returns from '../core/Returns.mjs' export * from './Types/generic.mjs' export const reducingOperation = { - 'undefined': () => u => u, - 'undefined,...any': () => (u, rest) => u, - 'any,undefined': () => (x, u) => u, - 'undefined,undefined': () => (u,v) => u, - any: () => x => x, + 'undefined': () => Returns('undefined', u => u), + 'undefined,...any': () => Returns('undefined', (u, rest) => u), + 'any,undefined': () => Returns('undefined', (x, u) => u), + 'undefined,undefined': () => Returns('undefined', (u,v) => u), + 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': ({ 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)) } diff --git a/src/generic/relational.mjs b/src/generic/relational.mjs index a1639a1..72de34c 100644 --- a/src/generic/relational.mjs +++ b/src/generic/relational.mjs @@ -1,34 +1,45 @@ +import Returns from '../core/Returns.mjs' + export const compare = { - 'undefined,undefined': () => () => 0 + 'undefined,undefined': () => Returns('NumInt', () => 0) } export const isZero = { - 'undefined': () => u => u === 0, - T: ({'equal(T,T)': eq, 'zero(T)': zr}) => t => eq(t, zr(t)) + 'undefined': () => Returns('boolean', u => u === 0), + T: ({ + T, + 'equal(T,T)': eq, + 'zero(T)': zr + }) => Returns('boolean', t => eq(t, zr(t))) } 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') if (resultant === 'any' || resultant in Templates) { return false } return equalTT(x,y) - } + }) } export const equalTT = { 'T,T': ({ 'compare(T,T)': cmp, '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 // do something like: // 'any,any': () => () => false // should only be hit for different types } export const unequal = { - 'any,any': ({equal}) => (x,y) => !(equal(x,y)) + 'any,any': ({equal}) => Returns('boolean', (x,y) => !(equal(x,y))) } export const larger = { @@ -36,7 +47,7 @@ export const larger = { 'compare(T,T)': cmp, 'one(T)' : uno, '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 = { @@ -45,10 +56,10 @@ export const largerEq = { 'one(T)' : uno, 'isZero(T)' : isZ, 'equalTT(T,T)': eq - }) => (x,y) => { + }) => Returns('boolean', (x,y) => { const c = cmp(x,y) return isZ(c) || eq(c, uno(y)) - } + }) } export const smaller = { @@ -57,10 +68,10 @@ export const smaller = { 'one(T)' : uno, 'isZero(T)' : isZ, unequal - }) => (x,y) => { + }) => Returns('boolean', (x,y) => { const c = cmp(x,y) return !isZ(c) && unequal(c, uno(y)) - } + }) } export const smallerEq = { @@ -68,5 +79,5 @@ export const smallerEq = { 'compare(T,T)': cmp, 'one(T)' : uno, unequal - }) => (x,y) => unequal(cmp(x,y), uno(y)) + }) => Returns('boolean', (x,y) => unequal(cmp(x,y), uno(y))) } diff --git a/src/generic/roundquotient.mjs b/src/generic/roundquotient.mjs index 5346882..9c2ba2b 100644 --- a/src/generic/roundquotient.mjs +++ b/src/generic/roundquotient.mjs @@ -1,3 +1,9 @@ +import Returns from '../core/Returns.mjs' + 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))) } diff --git a/src/generic/sign.mjs b/src/generic/sign.mjs index 769e2c9..cec73cd 100644 --- a/src/generic/sign.mjs +++ b/src/generic/sign.mjs @@ -1,3 +1,9 @@ +import Returns from '../core/Returns.mjs' + 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))) } diff --git a/src/generic/sqrt.mjs b/src/generic/sqrt.mjs index 21aa1d5..faea759 100644 --- a/src/generic/sqrt.mjs +++ b/src/generic/sqrt.mjs @@ -1,3 +1,4 @@ +import Returns from '../core/Returns.mjs' export * from './Types/generic.mjs' -export const sqrt = {undefined: () => () => undefined} +export const sqrt = {undefined: () => Returns('undefined', () => undefined)} diff --git a/src/generic/subtract.mjs b/src/generic/subtract.mjs index b048d0c..35dab22 100644 --- a/src/generic/subtract.mjs +++ b/src/generic/subtract.mjs @@ -1,3 +1,9 @@ +import Returns from '../core/Returns.mjs' + 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))) } diff --git a/test/_pocomath.mjs b/test/_pocomath.mjs index 4a63d55..3eff3d3 100644 --- a/test/_pocomath.mjs +++ b/test/_pocomath.mjs @@ -34,6 +34,11 @@ describe('The default full pocomath instance "math"', () => { math.add(3, math.complex(2.5, 1)), math.complex(5.5, 1)) assert.strictEqual( math.returnTypeOf('add', 'Complex,NumInt'), 'Complex') + // 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'), 'any') assert.strictEqual( math.returnTypeOf('chain', 'bigint'), 'Chain') assert.strictEqual( diff --git a/test/generic/_all.mjs b/test/generic/_all.mjs new file mode 100644 index 0000000..94829a4 --- /dev/null +++ b/test/generic/_all.mjs @@ -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') + }) +}) diff --git a/test/generic/fraction.mjs b/test/generic/fraction.mjs index 9af3b8d..f1fcd97 100644 --- a/test/generic/fraction.mjs +++ b/test/generic/fraction.mjs @@ -92,4 +92,10 @@ describe('fraction', () => { 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') + }) })