refactor: Add a stub for supplying return type information
With this commit, there's a rudimentary function that attaches return-type information, so it can be supplied, and PocomathInstance then proceeds to check for it everywhere necessary (and supply it in all the internal operations it builds). But so far in all of those place where it's checked for, it's just ignored. I think the next step will be just to put the return type info as a property on the function itself. That should actually ease all of the code.
This commit is contained in:
parent
207ac4330b
commit
4b28f8eac0
@ -1,7 +1,8 @@
|
||||
/* Core of pocomath: create an instance */
|
||||
import typed from 'typed-function'
|
||||
import {dependencyExtractor, generateTypeExtractor} from './extractors.mjs'
|
||||
import {makeChain} from './Chain.mjs'
|
||||
import {dependencyExtractor, generateTypeExtractor} from './extractors.mjs'
|
||||
import {R_, isReturnAnnotated} from './returns.mjs'
|
||||
import {typeListOfSignature, typesOfSignature, subsetOfKeys} from './utils.mjs'
|
||||
|
||||
const anySpec = {} // fixed dummy specification of 'any' type
|
||||
@ -82,7 +83,9 @@ export default class PocomathInstance {
|
||||
this._chainRepository = {} // place to store chainified functions
|
||||
|
||||
this._installFunctions({
|
||||
typeOf: {ground: {uses: new Set(), does: () => () => 'any'}}
|
||||
typeOf: {
|
||||
ground: {uses: new Set(), does: () => R_('string', () => 'any')}
|
||||
}
|
||||
})
|
||||
|
||||
this.joinTypes = this.joinTypes.bind(this)
|
||||
@ -381,7 +384,7 @@ export default class PocomathInstance {
|
||||
}
|
||||
// update the typeOf function
|
||||
const imp = {}
|
||||
imp[type] = {uses: new Set(), does: () => () => type}
|
||||
imp[type] = {uses: new Set(), does: () => R_('string', () => type)}
|
||||
this._installFunctions({typeOf: imp})
|
||||
}
|
||||
|
||||
@ -446,7 +449,10 @@ export default class PocomathInstance {
|
||||
}
|
||||
// update the typeOf function
|
||||
const imp = {}
|
||||
imp[type] = {uses: new Set(['T']), does: ({T}) => () => `${base}<${T}>`}
|
||||
imp[type] = {
|
||||
uses: new Set(['T']),
|
||||
does: ({T}) => R_('string', () => `${base}<${T}>`)
|
||||
}
|
||||
this._installFunctions({typeOf: imp})
|
||||
|
||||
// Nothing else actually happens until we match a template parameter
|
||||
@ -687,6 +693,9 @@ export default class PocomathInstance {
|
||||
}
|
||||
/* Now build the catchall implementation */
|
||||
const self = this
|
||||
/* For return-type annotation, we will have to fix this to
|
||||
propagate the return type. At the moment we are just bagging
|
||||
*/
|
||||
const patch = (refs) => (...args) => {
|
||||
/* We unbundle the rest arg if there is one */
|
||||
const regLength = args.length - 1
|
||||
@ -806,7 +815,12 @@ export default class PocomathInstance {
|
||||
}
|
||||
}
|
||||
// Finally ready to make the call.
|
||||
return behavior.does(innerRefs)(...args)
|
||||
let returnSpec = behavior.does(innerRefs)
|
||||
if (isReturnAnnotated(returnSpec)) {
|
||||
// for now just drop return type information
|
||||
returnSpec = returnSpec.via
|
||||
}
|
||||
return returnSpec(...args)
|
||||
}
|
||||
// The actual uses value needs to be a set:
|
||||
const outerUses = new Set(Object.values(simplifiedUses))
|
||||
@ -848,7 +862,12 @@ export default class PocomathInstance {
|
||||
_addTFimplementation(imps, signature, behavior) {
|
||||
const {uses, does} = behavior
|
||||
if (uses.length === 0) {
|
||||
imps[signature] = does()
|
||||
let returnSpec = does()
|
||||
if (isReturnAnnotated(returnSpec)) {
|
||||
// for now just dump the return information
|
||||
returnSpec = returnSpec.via
|
||||
}
|
||||
imps[signature] = returnSpec
|
||||
return
|
||||
}
|
||||
const refs = {}
|
||||
@ -908,7 +927,13 @@ export default class PocomathInstance {
|
||||
if (full_self_referential) {
|
||||
imps[signature] = this._typed.referToSelf(self => {
|
||||
refs.self = self
|
||||
return does(refs)
|
||||
let returnSpec = does(refs)
|
||||
if (isReturnAnnotated(returnSpec)) {
|
||||
// What are we going to do with the return type info in here?
|
||||
// Anyhow, drop it for now
|
||||
returnSpec = returnSpec.via
|
||||
}
|
||||
return returnSpec
|
||||
})
|
||||
return
|
||||
}
|
||||
@ -928,7 +953,12 @@ export default class PocomathInstance {
|
||||
}
|
||||
return
|
||||
}
|
||||
imps[signature] = does(refs)
|
||||
let returnSpec = does(refs)
|
||||
if (isReturnAnnotated(returnSpec)) {
|
||||
// drop return type for now
|
||||
returnSpec = returnSpec.via
|
||||
}
|
||||
imps[signature] = returnSpec
|
||||
}
|
||||
|
||||
_correctPartialSelfRefs(name, imps) {
|
||||
@ -963,7 +993,13 @@ export default class PocomathInstance {
|
||||
for (let i = 0; i < part_self_references.length; ++i) {
|
||||
refs[`self(${part_self_references[i]})`] = impls[i]
|
||||
}
|
||||
return does(refs)
|
||||
let returnSpec = does(refs)
|
||||
if (isReturnAnnotated(returnSpec)) {
|
||||
// What will we do with the return type info in here?
|
||||
// anyhow, drop it for now
|
||||
returnSpec = returnSpec.via
|
||||
}
|
||||
return returnSpec
|
||||
}
|
||||
)
|
||||
}
|
||||
|
11
src/core/returns.mjs
Normal file
11
src/core/returns.mjs
Normal file
@ -0,0 +1,11 @@
|
||||
/* Annotate a function with its return type */
|
||||
export function R_(type, fn) {
|
||||
return {returns: type, via: fn}
|
||||
}
|
||||
|
||||
export function isReturnAnnotated(x) {
|
||||
return x && typeof x === 'object' && 'returns' in x && 'via' in x
|
||||
}
|
||||
|
||||
export default R_
|
||||
|
@ -1,3 +1,37 @@
|
||||
import R_ from '../core/returns.mjs'
|
||||
export * from './Types/number.mjs'
|
||||
|
||||
export const negate = {number: () => n => -n}
|
||||
export const negate = {
|
||||
NumInt: () => R_('NumInt', n => -n),
|
||||
number: () => R_('number', n => -n)
|
||||
}
|
||||
/* Can we find a mechanism to avoid reiterating the definition
|
||||
for each number (sub)type? Maybe something like:
|
||||
|
||||
export const negate = {'T:number': ({T}) => R_(T, n => -n)}
|
||||
|
||||
where 'T:number' is a new notation that means "T is a (non-strict) subtype
|
||||
of number". That doesn't look too bad, but if we went down that route, we
|
||||
might also need to make sure that if we ever implemented a PositiveNumber
|
||||
type and a NegativeNumber type then we could say
|
||||
|
||||
export const negate = {
|
||||
PositiveNumber: () => R_('NegativeNumber', n => -n),
|
||||
NegativeNumber: () => R_('PositiveNumber', n => -n),
|
||||
'T:number': ({T}) => R_(T, n => -n)
|
||||
}
|
||||
|
||||
|
||||
This just gets worse if there are also PosNumInt and NegNumInt types;
|
||||
maybe Positive<T>, Negative<T>, and Zero<T> are template types, so that
|
||||
we could say:
|
||||
|
||||
export const negate = {
|
||||
'Positive<T:number>': ({'Negative<T>': negT}) => R_(negT, n => -n),
|
||||
'Negative<T:number>': ({'Positive<T>': posT}) => R_(posT, n => -n),
|
||||
T:number: ({T}) => R_(T, n => -n)
|
||||
}
|
||||
|
||||
But all of that is pretty speculative, for now let's just go with writing
|
||||
out the different types.
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user