feat: Return type annotations #53
@ -1,7 +1,8 @@
|
|||||||
/* Core of pocomath: create an instance */
|
/* Core of pocomath: create an instance */
|
||||||
import typed from 'typed-function'
|
import typed from 'typed-function'
|
||||||
import {dependencyExtractor, generateTypeExtractor} from './extractors.mjs'
|
|
||||||
import {makeChain} from './Chain.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'
|
import {typeListOfSignature, typesOfSignature, subsetOfKeys} from './utils.mjs'
|
||||||
|
|
||||||
const anySpec = {} // fixed dummy specification of 'any' type
|
const anySpec = {} // fixed dummy specification of 'any' type
|
||||||
@ -82,7 +83,9 @@ export default class PocomathInstance {
|
|||||||
this._chainRepository = {} // place to store chainified functions
|
this._chainRepository = {} // place to store chainified functions
|
||||||
|
|
||||||
this._installFunctions({
|
this._installFunctions({
|
||||||
typeOf: {ground: {uses: new Set(), does: () => () => 'any'}}
|
typeOf: {
|
||||||
|
ground: {uses: new Set(), does: () => R_('string', () => 'any')}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
this.joinTypes = this.joinTypes.bind(this)
|
this.joinTypes = this.joinTypes.bind(this)
|
||||||
@ -381,7 +384,7 @@ export default class PocomathInstance {
|
|||||||
}
|
}
|
||||||
// update the typeOf function
|
// update the typeOf function
|
||||||
const imp = {}
|
const imp = {}
|
||||||
imp[type] = {uses: new Set(), does: () => () => type}
|
imp[type] = {uses: new Set(), does: () => R_('string', () => type)}
|
||||||
this._installFunctions({typeOf: imp})
|
this._installFunctions({typeOf: imp})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -446,7 +449,10 @@ export default class PocomathInstance {
|
|||||||
}
|
}
|
||||||
// update the typeOf function
|
// update the typeOf function
|
||||||
const imp = {}
|
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})
|
this._installFunctions({typeOf: imp})
|
||||||
|
|
||||||
// Nothing else actually happens until we match a template parameter
|
// Nothing else actually happens until we match a template parameter
|
||||||
@ -687,6 +693,9 @@ export default class PocomathInstance {
|
|||||||
}
|
}
|
||||||
/* Now build the catchall implementation */
|
/* Now build the catchall implementation */
|
||||||
const self = this
|
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) => {
|
const patch = (refs) => (...args) => {
|
||||||
/* We unbundle the rest arg if there is one */
|
/* We unbundle the rest arg if there is one */
|
||||||
const regLength = args.length - 1
|
const regLength = args.length - 1
|
||||||
@ -806,7 +815,12 @@ export default class PocomathInstance {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Finally ready to make the call.
|
// 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:
|
// The actual uses value needs to be a set:
|
||||||
const outerUses = new Set(Object.values(simplifiedUses))
|
const outerUses = new Set(Object.values(simplifiedUses))
|
||||||
@ -848,7 +862,12 @@ export default class PocomathInstance {
|
|||||||
_addTFimplementation(imps, signature, behavior) {
|
_addTFimplementation(imps, signature, behavior) {
|
||||||
const {uses, does} = behavior
|
const {uses, does} = behavior
|
||||||
if (uses.length === 0) {
|
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
|
return
|
||||||
}
|
}
|
||||||
const refs = {}
|
const refs = {}
|
||||||
@ -908,7 +927,13 @@ export default class PocomathInstance {
|
|||||||
if (full_self_referential) {
|
if (full_self_referential) {
|
||||||
imps[signature] = this._typed.referToSelf(self => {
|
imps[signature] = this._typed.referToSelf(self => {
|
||||||
refs.self = 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
|
return
|
||||||
}
|
}
|
||||||
@ -928,7 +953,12 @@ export default class PocomathInstance {
|
|||||||
}
|
}
|
||||||
return
|
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) {
|
_correctPartialSelfRefs(name, imps) {
|
||||||
@ -963,7 +993,13 @@ export default class PocomathInstance {
|
|||||||
for (let i = 0; i < part_self_references.length; ++i) {
|
for (let i = 0; i < part_self_references.length; ++i) {
|
||||||
refs[`self(${part_self_references[i]})`] = impls[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 * 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