feat: Return type annotations #53
@ -8,13 +8,8 @@ export const abs = {
|
||||
'absquare(T)': baseabsq,
|
||||
'absquare(Complex<T>)': absq
|
||||
}) => {
|
||||
const pm = sqrt.fromInstance
|
||||
if (typeof pm === 'undefined') {
|
||||
// Just checking for the dependencies, return value is irrelevant
|
||||
return undefined
|
||||
}
|
||||
const midType = returnTypeOf(baseabsq)
|
||||
const sqrtImp = pm.resolve('sqrt', midType, sqrt)
|
||||
const sqrtImp = sqrt.fromInstance.resolve('sqrt', midType, sqrt)
|
||||
let retType = returnTypeOf(sqrtImp)
|
||||
if (retType.includes('|')) {
|
||||
// This is a bit of a hack, as it relies on all implementations of
|
||||
|
@ -9,13 +9,9 @@ export const absquare = {
|
||||
// we extract the needed implementation below.
|
||||
'self(T)': absq
|
||||
}) => {
|
||||
const pm = add.fromInstance
|
||||
if (typeof pm === 'undefined') {
|
||||
// Just checking the dependencies, return value irrelevant
|
||||
return undefined
|
||||
}
|
||||
const midType = returnTypeOf(absq)
|
||||
const addImp = pm.resolve('add', `${midType},${midType}`, add)
|
||||
const addImp = add.fromInstance.resolve(
|
||||
'add', `${midType},${midType}`, add)
|
||||
return Returns(
|
||||
returnTypeOf(addImp), z => addImp(absq(z.re), absq(z.im)))
|
||||
}
|
||||
|
@ -7,11 +7,76 @@ import {typeListOfSignature, typesOfSignature, subsetOfKeys} from './utils.mjs'
|
||||
|
||||
const anySpec = {} // fixed dummy specification of 'any' type
|
||||
|
||||
/* Template/signature parsing stuff; should probably be moved to a
|
||||
* separate file, but it's a bit interleaved at the moment
|
||||
*/
|
||||
|
||||
const theTemplateParam = 'T' // First pass: only allow this one exact parameter
|
||||
const restTemplateParam = `...${theTemplateParam}`
|
||||
const templateCall = `<${theTemplateParam}>`
|
||||
const templateFromParam = 'U' // For defining covariant conversions
|
||||
|
||||
/* returns the pair [base, instance] for a template type. If the type
|
||||
* is not a template, instance is undefined
|
||||
*/
|
||||
const templatePattern = /^\s*([^<\s]*)\s*<\s*(\S*)\s*>\s*$/
|
||||
function splitTemplate(type) {
|
||||
if (!(type.includes('<'))) return [type, undefined]
|
||||
const results = templatePattern.exec(type)
|
||||
return [results[1], results[2]]
|
||||
}
|
||||
/* Returns the instance such that type is template instantiated for that
|
||||
* instance.
|
||||
*/
|
||||
function whichInstance(type, template) {
|
||||
if (template === theTemplateParam) return type
|
||||
if (type === template) return ''
|
||||
if (!(template.includes(templateCall))) {
|
||||
throw new TypeError(
|
||||
`Type ${template} is not a template, so can't produce ${type}`)
|
||||
}
|
||||
const [typeBase, typeInstance] = splitTemplate(type)
|
||||
if (!typeInstance) {
|
||||
throw new TypeError(
|
||||
`Type ${type} not from a template, so isn't instance of ${template}`)
|
||||
}
|
||||
const [tempBase, tempInstance] = splitTemplate(template)
|
||||
if (typeBase !== tempBase) {
|
||||
throw new TypeError(
|
||||
`Type ${type} has wrong top-level base to be instance of ${template}`)
|
||||
}
|
||||
return whichInstance(typeInstance, tempInstance)
|
||||
}
|
||||
/* Same as above, but for signatures */
|
||||
function whichSigInstance(sig, tempsig) {
|
||||
const sigTypes = typeListOfSignature(sig)
|
||||
const tempTypes = typeListOfSignature(tempsig)
|
||||
const sigLength = sigTypes.length
|
||||
if (sigLength === 0) {
|
||||
throw new TypeError("No types in signature, so can't determine instance")
|
||||
}
|
||||
if (sigLength !== tempTypes.length) {
|
||||
throw new TypeError(`Signatures ${sig} and ${tempsig} differ in length`)
|
||||
}
|
||||
let maybeInstance = whichInstance(sigTypes[0], tempTypes[0])
|
||||
for (let i = 1; i < sigLength; ++i) {
|
||||
const currInstance = whichInstance(sigTypes[i], tempTypes[i])
|
||||
if (maybeInstance) {
|
||||
if (currInstance && currInstance !== maybeInstance) {
|
||||
throw new TypeError(
|
||||
`Inconsistent instantiation of ${sig} from ${tempsig}`)
|
||||
}
|
||||
} else {
|
||||
maybeInstance = currInstance
|
||||
}
|
||||
}
|
||||
if (!maybeInstance) {
|
||||
throw new TypeError(
|
||||
`Signature ${sig} identical to ${tempsig}, not an instance`)
|
||||
}
|
||||
return maybeInstance
|
||||
}
|
||||
|
||||
/* Returns a new signature just like sig but with the parameter replaced by
|
||||
* the type
|
||||
*/
|
||||
@ -202,7 +267,10 @@ export default class PocomathInstance {
|
||||
const stdimps = {}
|
||||
for (const [signature, does] of Object.entries(spec)) {
|
||||
const uses = new Set()
|
||||
does(dependencyExtractor(uses))
|
||||
try {
|
||||
does(dependencyExtractor(uses))
|
||||
} catch {
|
||||
}
|
||||
stdimps[signature] = {uses, does}
|
||||
}
|
||||
stdFunctions[item] = stdimps
|
||||
@ -334,7 +402,7 @@ export default class PocomathInstance {
|
||||
* the corresponding changes to the _typed object immediately
|
||||
*/
|
||||
installType = Returns('void', function(type, spec) {
|
||||
const parts = type.split(/[<,>]/)
|
||||
const parts = type.split(/[<,>]/).map(s => s.trim())
|
||||
if (this._templateParam(parts[0])) {
|
||||
throw new SyntaxError(
|
||||
`Type name '${type}' reserved for template parameter`)
|
||||
@ -400,14 +468,14 @@ export default class PocomathInstance {
|
||||
}
|
||||
|
||||
/* Add the conversion in the metaverse if need be: */
|
||||
const toParts = nextSuper.split('<', 2)
|
||||
if (toParts.length > 1) {
|
||||
const fromParts = from.split('<', 2)
|
||||
if (fromParts.length === 1 || fromParts[0] !== toParts[0]) {
|
||||
const [toBase, toInstance] = splitTemplate(nextSuper)
|
||||
if (toInstance) {
|
||||
const [fromBase, fromInstance] = splitTemplate(from)
|
||||
if (!fromBase || fromBase !== toBase) {
|
||||
this._metafy(from)
|
||||
try {
|
||||
this._metaTyped.addConversion(
|
||||
{from, to: toParts[0], convert: spec.from[from]})
|
||||
{from, to: toBase, convert: spec.from[from]})
|
||||
} catch {
|
||||
}
|
||||
}
|
||||
@ -438,12 +506,12 @@ export default class PocomathInstance {
|
||||
})
|
||||
this._invalidateDependents(':' + nextSuper)
|
||||
/* Add the conversion in the metaverse if need be: */
|
||||
const toParts = nextSuper.split('<', 2)
|
||||
if (toParts.length > 1 && base !== toParts[0]) {
|
||||
const [toBase, toInstance] = splitTemplate(nextSuper)
|
||||
if (toInstance && base !== toBase) {
|
||||
this._metafy(type)
|
||||
this._metaTyped.addConversion({
|
||||
from: type,
|
||||
to: toParts[0],
|
||||
to: toBase,
|
||||
convert: this.Types[to].from[fromtype]
|
||||
})
|
||||
}
|
||||
@ -559,7 +627,7 @@ export default class PocomathInstance {
|
||||
|
||||
/* Used internally to install a template type */
|
||||
_installTemplateType(type, spec) {
|
||||
const base = type.split('<')[0]
|
||||
const [base] = splitTemplate(type)
|
||||
/* For now, just allow a single template per base type; that
|
||||
* might need to change later:
|
||||
*/
|
||||
@ -760,7 +828,7 @@ export default class PocomathInstance {
|
||||
let keep = true
|
||||
for (const type of typesOfSignature(entry[0])) {
|
||||
if (type in this.Types) continue
|
||||
const baseType = type.split('<')[0]
|
||||
const [baseType] = splitTemplate(type)
|
||||
if (baseType in this.Templates) continue
|
||||
keep = false
|
||||
break
|
||||
@ -857,7 +925,7 @@ export default class PocomathInstance {
|
||||
othertype, theTemplateParam, '')
|
||||
let otherTypeCollection = [othertype]
|
||||
if (testType !== othertype) {
|
||||
const base = othertype.split('<',1)[0]
|
||||
const [base] = splitTemplate(othertype)
|
||||
otherTypeCollection = this._instantiationsOf[base]
|
||||
}
|
||||
for (const possibility of otherTypeCollection) {
|
||||
@ -873,43 +941,7 @@ export default class PocomathInstance {
|
||||
}
|
||||
|
||||
for (const instType of instantiationSet) {
|
||||
if (!(instType in this.Types)) continue
|
||||
if (this.Types[instType] === anySpec) continue
|
||||
if (instType in behavior.hasInstantiations) continue
|
||||
const signature =
|
||||
substituteInSignature(rawSignature, theTemplateParam, instType)
|
||||
/* Don't override an explicit implementation: */
|
||||
if (signature in imps) continue
|
||||
/* Don't go too deep */
|
||||
let maxdepth = 0
|
||||
for (const argType in typeListOfSignature(signature)) {
|
||||
const depth = argType.split('<').length
|
||||
if (depth > maxdepth) maxdepth = depth
|
||||
}
|
||||
if (maxdepth > this._maxDepthSeen + 1) continue
|
||||
/* All right, go ahead and instantiate */
|
||||
const uses = new Set()
|
||||
for (const dep of behavior.uses) {
|
||||
if (this._templateParam(dep)) continue
|
||||
uses.add(substituteInSignature(dep, theTemplateParam, instType))
|
||||
}
|
||||
const patch = (refs) => {
|
||||
const innerRefs = {}
|
||||
for (const dep of behavior.uses) {
|
||||
if (this._templateParam(dep)) {
|
||||
innerRefs[dep] = instType
|
||||
} else {
|
||||
const outerName = substituteInSignature(
|
||||
dep, theTemplateParam, instType)
|
||||
innerRefs[dep] = refs[outerName]
|
||||
}
|
||||
}
|
||||
return behavior.does(innerRefs)
|
||||
}
|
||||
this._addTFimplementation(tf_imps, signature, {uses, does: patch})
|
||||
tf_imps[signature]._pocoSignature = rawSignature
|
||||
tf_imps[signature]._pocoInstance = instType
|
||||
behavior.hasInstantiations[instType] = signature
|
||||
this._instantiateTemplateImplementation(name, rawSignature, instType)
|
||||
}
|
||||
/* Now add the catchall signature */
|
||||
/* (Not needed if if it's a bounded template) */
|
||||
@ -1031,7 +1063,7 @@ export default class PocomathInstance {
|
||||
for (const possibility of returnType.split('|')) {
|
||||
const instantiated = self._maybeInstantiate(possibility)
|
||||
if (instantiated) {
|
||||
const tempBase = instantiated.split('<',1)[0]
|
||||
const [tempBase] = splitTemplate(instantiated)
|
||||
self._invalidateDependents(':' + tempBase)
|
||||
}
|
||||
}
|
||||
@ -1047,6 +1079,7 @@ export default class PocomathInstance {
|
||||
}
|
||||
Object.defineProperty(
|
||||
patchFunc, 'name', {value: `${name}(${signature})`})
|
||||
patchFunc._pocoSignature = rawSignature
|
||||
return patchFunc
|
||||
}
|
||||
Object.defineProperty(
|
||||
@ -1083,12 +1116,12 @@ export default class PocomathInstance {
|
||||
let tf
|
||||
if (Object.keys(tf_imps).length > 0) {
|
||||
tf = this._typed(name, tf_imps)
|
||||
Object.defineProperty(tf, 'fromInstance', {value: this})
|
||||
tf.fromInstance = this
|
||||
}
|
||||
let metaTF
|
||||
if (Object.keys(meta_imps).length > 0) {
|
||||
metaTF = this._metaTyped(name, meta_imps)
|
||||
Object.defineProperty(metaTF, 'fromInstance', {value: this})
|
||||
metaTF.fromInstance = this
|
||||
}
|
||||
this._meta[name] = metaTF
|
||||
|
||||
@ -1137,11 +1170,58 @@ export default class PocomathInstance {
|
||||
return undefined // no such type
|
||||
}
|
||||
// it's a template type, turn it into a template and an arg
|
||||
let base = type.split('<',1)[0]
|
||||
const arg = type.slice(base.length+1, -1)
|
||||
let [base, arg] = splitTemplate(type)
|
||||
return this.instantiateTemplate(base, arg)
|
||||
}
|
||||
|
||||
/* Generate and include a template instantiation for operation name
|
||||
* for the template signature templateSignature instantiated for
|
||||
* instanceType, returning the resulting implementation.
|
||||
*/
|
||||
_instantiateTemplateImplementation(name, templateSignature, instanceType) {
|
||||
if (!(instanceType in this.Types)) return undefined
|
||||
if (this.Types[instanceType] === anySpec) return undefined
|
||||
const imps = this._imps[name]
|
||||
const behavior = imps[templateSignature]
|
||||
if (instanceType in behavior.hasInstantiations) return undefined
|
||||
const signature = substituteInSignature(
|
||||
templateSignature, theTemplateParam, instanceType)
|
||||
/* Don't override an explicit implementation: */
|
||||
if (signature in imps) return undefined
|
||||
/* Don't go too deep */
|
||||
let maxdepth = 0
|
||||
for (const argType in typeListOfSignature(signature)) {
|
||||
const depth = argType.split('<').length
|
||||
if (depth > maxdepth) maxdepth = depth
|
||||
}
|
||||
if (maxdepth > this._maxDepthSeen + 1) return undefined
|
||||
/* All right, go ahead and instantiate */
|
||||
const uses = new Set()
|
||||
for (const dep of behavior.uses) {
|
||||
if (this._templateParam(dep)) continue
|
||||
uses.add(substituteInSignature(dep, theTemplateParam, instanceType))
|
||||
}
|
||||
const patch = (refs) => {
|
||||
const innerRefs = {}
|
||||
for (const dep of behavior.uses) {
|
||||
if (this._templateParam(dep)) {
|
||||
innerRefs[dep] = instanceType
|
||||
} else {
|
||||
const outerName = substituteInSignature(
|
||||
dep, theTemplateParam, instanceType)
|
||||
innerRefs[dep] = refs[outerName]
|
||||
}
|
||||
}
|
||||
return behavior.does(innerRefs)
|
||||
}
|
||||
const tf_imps = this._TFimps[name]
|
||||
this._addTFimplementation(tf_imps, signature, {uses, does: patch})
|
||||
tf_imps[signature]._pocoSignature = templateSignature
|
||||
tf_imps[signature]._pocoInstance = instanceType
|
||||
behavior.hasInstantiations[instanceType] = signature
|
||||
return tf_imps[signature]
|
||||
}
|
||||
|
||||
/* Adapts Pocomath-style behavior specification (uses, does) for signature
|
||||
* to typed-function implementations and inserts the result into plain
|
||||
* object imps
|
||||
@ -1254,6 +1334,7 @@ export default class PocomathInstance {
|
||||
refs.self = self
|
||||
const implementation = does(refs)
|
||||
Object.defineProperty(implementation, 'name', {value: does.name})
|
||||
implementation.fromInstance = this
|
||||
// What are we going to do with the return type info in here?
|
||||
return implementation
|
||||
})
|
||||
@ -1271,11 +1352,13 @@ export default class PocomathInstance {
|
||||
deferred: true,
|
||||
builtRefs: refs,
|
||||
sigDoes: does,
|
||||
fromInstance: this,
|
||||
psr: part_self_references
|
||||
}
|
||||
return
|
||||
}
|
||||
const implementation = does(refs)
|
||||
implementation.fromInstance = this
|
||||
// could do something with return type information here?
|
||||
imps[signature] = implementation
|
||||
}
|
||||
@ -1310,7 +1393,22 @@ export default class PocomathInstance {
|
||||
name, this._imps[name], neededSig)
|
||||
if (foundSig) {
|
||||
const match = this._pocoFindSignature(name, neededSig)
|
||||
refs[`self(${neededSig})`] = match.implementation
|
||||
const neededTemplate = match.fn._pocoSignature
|
||||
const neededInstance = whichSigInstance(
|
||||
neededSig, neededTemplate)
|
||||
const neededImplementation =
|
||||
this._instantiateTemplateImplementation(
|
||||
name, neededTemplate, neededInstance)
|
||||
if (!neededImplementation) {
|
||||
refs[`self(${neededSig})`] = match.implementation
|
||||
} else {
|
||||
if (typeof neededImplementation === 'function') {
|
||||
refs[`self(${neededSig})`] = neededImplementation
|
||||
} else {
|
||||
corrected_self_references.push(neededSig)
|
||||
remaining_self_references.push(neededSig)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new Error(
|
||||
'Implement inexact self-reference in typed-function for '
|
||||
@ -1335,6 +1433,7 @@ export default class PocomathInstance {
|
||||
}
|
||||
imps[aSignature]._pocoSignature = deferral._pocoSignature
|
||||
imps[aSignature]._pocoInstance = deferral._pocoInstance
|
||||
imps[aSignature].fromInstance = deferral.fromInstance
|
||||
}
|
||||
}
|
||||
|
||||
@ -1343,8 +1442,7 @@ export default class PocomathInstance {
|
||||
* in the instance.
|
||||
*/
|
||||
_ensureTemplateTypes(template, type) {
|
||||
const base = template.split('<', 1)[0]
|
||||
const arg = template.slice(base.length + 1, -1)
|
||||
const [base, arg] = splitTemplate(template)
|
||||
if (!arg) {
|
||||
throw new Error(
|
||||
'Implementation error in _ensureTemplateTypes', template, type)
|
||||
@ -1481,7 +1579,7 @@ export default class PocomathInstance {
|
||||
if (otherType === 'any') continue
|
||||
if (myType === otherType) continue
|
||||
if (otherType in this.Templates) {
|
||||
const myBase = myType.split('<',1)[0]
|
||||
const [myBase] = splitTemplate(myType)
|
||||
if (myBase === otherType) continue
|
||||
if (this.instantiateTemplate(otherType, myType)) {
|
||||
let dummy
|
||||
@ -1525,10 +1623,24 @@ export default class PocomathInstance {
|
||||
let allMatched = true
|
||||
const implTypes = typeListOfSignature(implSig)
|
||||
for (let i = 0; i < wantTypes.length; ++i) {
|
||||
if (wantTypes[i] == implTypes[i]
|
||||
|| this.isSubtypeOf(wantTypes[i], implTypes[i])) continue
|
||||
allMatched = false
|
||||
break
|
||||
const implIndex = Math.min(i, implTypes.length - 1)
|
||||
let implType = implTypes[implIndex]
|
||||
if (implIndex < i) {
|
||||
if (implType.slice(0,3) !== '...') {
|
||||
// ran out of arguments in impl
|
||||
allMatched = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if (implType.slice(0,3) === '...') {
|
||||
implType = implType.slice(3)
|
||||
}
|
||||
const hasMatch = implType.split('|').some(
|
||||
t => (wantTypes[i] === t || this.isSubtypeOf(wantTypes[i], t)))
|
||||
if (!hasMatch) {
|
||||
allMatched = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if (allMatched) return details
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
/* A template type representing a homeogeneous tuple of elements */
|
||||
import PocomathInstance from '../../core/PocomathInstance.mjs'
|
||||
import {Returns, returnTypeOf} from '../../core/Returns.mjs'
|
||||
|
||||
const Tuple = new PocomathInstance('Tuple')
|
||||
|
||||
@ -33,50 +34,66 @@ Tuple.promoteUnary = {
|
||||
'Tuple<T>': ({
|
||||
'self(T)': me,
|
||||
tuple
|
||||
}) => t => tuple(...(t.elts.map(x => me(x)))) // NOTE: this must use
|
||||
// the inner arrow function to drop additional arguments that Array.map
|
||||
// supplies, as otherwise the wrong signature of `me` might be used.
|
||||
}) => {
|
||||
const compType = me.fromInstance.joinTypes(
|
||||
returnTypeOf(me).split('|'), 'convert')
|
||||
return Returns(
|
||||
`Tuple<${compType}>`, t => tuple(...(t.elts.map(x => me(x)))))
|
||||
}
|
||||
}
|
||||
|
||||
Tuple.promoteBinaryUnary = {
|
||||
'Tuple<T>,Tuple<T>': ({'self(T,T)': meB, 'self(T)': meU, tuple}) => (s,t) => {
|
||||
let i = -1
|
||||
let result = []
|
||||
while (true) {
|
||||
i += 1
|
||||
if (i < s.elts.length) {
|
||||
if (i < t.elts.length) result.push(meB(s.elts[i], t.elts[i]))
|
||||
else result.push(meU(s.elts[i]))
|
||||
continue
|
||||
'Tuple<T>,Tuple<T>': ({'self(T,T)': meB, 'self(T)': meU, tuple}) => {
|
||||
const compTypes = returnTypeOf(meB).split('|').concat(
|
||||
returnTypeOf(meU).split('|'))
|
||||
const compType = meU.fromInstance.joinTypes(compTypes, 'convert')
|
||||
return Returns(`Tuple<${compType}>`, (s,t) => {
|
||||
let i = -1
|
||||
let result = []
|
||||
while (true) {
|
||||
i += 1
|
||||
if (i < s.elts.length) {
|
||||
if (i < t.elts.length) result.push(meB(s.elts[i], t.elts[i]))
|
||||
else result.push(meU(s.elts[i]))
|
||||
continue
|
||||
}
|
||||
if (i < t.elts.length) result.push(meU(t.elts[i]))
|
||||
else break
|
||||
}
|
||||
if (i < t.elts.length) result.push(meU(t.elts[i]))
|
||||
else break
|
||||
}
|
||||
return tuple(...result)
|
||||
return tuple(...result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Tuple.promoteBinary = {
|
||||
'Tuple<T>,Tuple<T>': ({'self(T,T)': meB, tuple}) => (s,t) => {
|
||||
const lim = Math.max(s.elts.length, t.elts.length)
|
||||
const result = []
|
||||
for (let i = 0; i < lim; ++i) {
|
||||
result.push(meB(s.elts[i], t.elts[i]))
|
||||
}
|
||||
return tuple(...result)
|
||||
'Tuple<T>,Tuple<T>': ({'self(T,T)': meB, tuple}) => {
|
||||
const compType = meB.fromInstance.joinTypes(
|
||||
returnTypeOf(meB).split('|'))
|
||||
return Returns(`Tuple<${compType}>`, (s,t) => {
|
||||
const lim = Math.max(s.elts.length, t.elts.length)
|
||||
const result = []
|
||||
for (let i = 0; i < lim; ++i) {
|
||||
result.push(meB(s.elts[i], t.elts[i]))
|
||||
}
|
||||
return tuple(...result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Tuple.promoteBinaryStrict = {
|
||||
'Tuple<T>,Tuple<T>': ({'self(T,T)': meB, tuple}) => (s,t) => {
|
||||
if (s.elts.length !== t.elts.length) {
|
||||
throw new RangeError('Tuple length mismatch') // get name of self ??
|
||||
}
|
||||
const result = []
|
||||
for (let i = 0; i < s.elts.length; ++i) {
|
||||
result.push(meB(s.elts[i], t.elts[i]))
|
||||
}
|
||||
return tuple(...result)
|
||||
'Tuple<T>,Tuple<T>': ({'self(T,T)': meB, tuple}) => {
|
||||
const compType = meB.fromInstance.joinTypes(
|
||||
returnTypeOf(meB).split('|'))
|
||||
return Returns(`Tuple<${compType}>`, (s,t) => {
|
||||
if (s.elts.length !== t.elts.length) {
|
||||
throw new RangeError('Tuple length mismatch') // get name of self ??
|
||||
}
|
||||
const result = []
|
||||
for (let i = 0; i < s.elts.length; ++i) {
|
||||
result.push(meB(s.elts[i], t.elts[i]))
|
||||
}
|
||||
return tuple(...result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,11 +1,16 @@
|
||||
import Returns from '../core/Returns.mjs'
|
||||
|
||||
export * from './Types/Tuple.mjs'
|
||||
|
||||
export const equalTT = {
|
||||
'Tuple<T>,Tuple<T>': ({'self(T,T)': me, 'length(Tuple)': len}) => (s,t) => {
|
||||
'Tuple<T>,Tuple<T>': ({
|
||||
'self(T,T)': me,
|
||||
'length(Tuple)': len
|
||||
}) => Returns('boolean', (s,t) => {
|
||||
if (len(s) !== len(t)) return false
|
||||
for (let i = 0; i < len(s); ++i) {
|
||||
if (!me(s.elts[i], t.elts[i])) return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -1,7 +1,10 @@
|
||||
import Returns from '../core/Returns.mjs'
|
||||
|
||||
export {Tuple} from './Types/Tuple.mjs'
|
||||
|
||||
export const isZero = {
|
||||
'Tuple<T>': ({'self(T)': me}) => t => t.elts.every(e => me(e))
|
||||
'Tuple<T>': ({'self(T)': me}) => Returns(
|
||||
'boolean', t => t.elts.every(e => me(e)))
|
||||
// Note we can't just say `every(me)` above since every invokes its
|
||||
// callback with more arguments, which then violates typed-function's
|
||||
// signature for `me`
|
||||
|
@ -1,6 +1,10 @@
|
||||
import Returns from '../core/Returns.mjs'
|
||||
|
||||
export {Tuple} from './Types/Tuple.mjs'
|
||||
|
||||
/* The purpose of the template argument is to ensure that all of the args
|
||||
* are convertible to the same type.
|
||||
*/
|
||||
export const tuple = {'...T': () => args => ({elts: args})}
|
||||
export const tuple = {
|
||||
'...T': ({T}) => Returns(`Tuple<${T}>`, args => ({elts: args}))
|
||||
}
|
||||
|
@ -54,6 +54,9 @@ describe('tuple', () => {
|
||||
assert.deepStrictEqual(
|
||||
math.subtract(math.tuple(3n,4n,5n), math.tuple(2n,1n,0n)),
|
||||
math.tuple(1n,3n,5n))
|
||||
assert.deepStrictEqual(
|
||||
math.returnTypeOf('subtract', 'Tuple<bigint>,Tuple<bigint>'),
|
||||
'Tuple<bigint>')
|
||||
assert.throws(
|
||||
() => math.subtract(math.tuple(5,6), math.tuple(7)),
|
||||
/RangeError/)
|
||||
@ -104,9 +107,16 @@ describe('tuple', () => {
|
||||
})
|
||||
|
||||
it('supports sqrt', () => {
|
||||
const mixedTuple = math.tuple(2, math.complex(0,2), 1.5)
|
||||
assert.deepStrictEqual(
|
||||
math.sqrt(math.tuple(4,-4,2.25)),
|
||||
math.tuple(2, math.complex(0,2), 1.5))
|
||||
mixedTuple,
|
||||
math.tuple(math.complex(2), math.complex(0,2), math.complex(1.5)))
|
||||
assert.strictEqual(
|
||||
math.returnTypeOf('tuple', 'NumInt, Complex<NumInt>, number'),
|
||||
'Tuple<Complex<number>>')
|
||||
assert.deepStrictEqual(math.sqrt(math.tuple(4,-4,2.25)), mixedTuple)
|
||||
assert.strictEqual(
|
||||
math.returnTypeOf('sqrt', 'Tuple<NumInt>'), 'Tuple<Complex<number>>')
|
||||
})
|
||||
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user