Return type annotations #52

Closed
opened 2022-08-10 15:01:22 +00:00 by glen · 1 comment
Owner

It would be interesting to add to this a proof of concept of return-type annotations. The ides is that an implementation of sqrt that is currently:

   number: ({
      config,
      'complex(number,number)': cplx,
      'negate(number)': neg}) => {
      if (config.predictable || !cplx) {
         return n => isNaN(n) ? NaN : Math.sqrt(n)
      }
      return n => {
         if (isNaN(n)) return NaN
         if (n >= 0) return Math.sqrt(n)
         return cplx(0, Math.sqrt(neg(n)))
      }
   }

would become

   number: ({
      config,
      'complex(number,number)': cplx,
      'negate(number)': neg}) => {
      if (config.predictable || !cplx) {
         return R_('number', n => isNaN(n) ? NaN : Math.sqrt(n))
      }
      return R_('number|Complex<number>', n => {
         if (isNaN(n)) return NaN
         if (n >= 0) return Math.sqrt(n)
         return cplx(0, Math.sqrt(neg(n)))
      })
   }

and a short and sweet implementation like one from negate:

{bigint: () => b => -b}

would become

{bigint: () => R_('bigint', b => -b)}

(The symbol R_ was proposed above to be as short and sweet as possible since idealy the function name would be '->' ;-). But 'yields' is another possibility, as well as a straight structural notation like:

{bigint: () => ['bigint', b => -b]}

The advantage of using a function symbol like R_ is that there's an opportunity to put more code there if it ends up being helpful; the advantage of a purely structural notation is that there is no symbol to import and it is very compact.

Note the exampe of sqrt was chosen above to show that the return type would need to come after the dependencies, not on the outer level, because the return type can depend on the dependencies as supplied. (In fact, in some sense the latter part of the sqrt implementation should be something like:

      const complexReturn = returnType(cplx)
      return R_('number|' + complexReturn, n => {
         ... implementation ...
      })

but that's now getting a bit cumbersome... maybe R_ could be made cleverer so that you
could just write:

      return R_(['number', cplx], n => {
         ... imp ...
      }

That now seems like the way to go.

Besides allowing a typescript declaration generator as per https://github.com/josdejong/typed-function/issues/123, return type annotation could be used to

  • provide type-checking of expressions in the parser
  • guide simplification of expressions (and thereby eliminate the assumptions option)
  • provide a compose function: math.compose(math.sqrt, math.negate) which produces a function that computes sqrt(-x) but only performs type dispatch once, when negate gets the argument, but arranges to call the proper implementation of sqrt on the result without having to typecheck. This compose operation could be used to provide the semantics of expressions, nearly eliminating the typechecking that would go on in evaluating an expression.

So it seems well worth the effort.

It would be interesting to add to this a proof of concept of return-type annotations. The ides is that an implementation of sqrt that is currently: ``` number: ({ config, 'complex(number,number)': cplx, 'negate(number)': neg}) => { if (config.predictable || !cplx) { return n => isNaN(n) ? NaN : Math.sqrt(n) } return n => { if (isNaN(n)) return NaN if (n >= 0) return Math.sqrt(n) return cplx(0, Math.sqrt(neg(n))) } } ``` would become ``` number: ({ config, 'complex(number,number)': cplx, 'negate(number)': neg}) => { if (config.predictable || !cplx) { return R_('number', n => isNaN(n) ? NaN : Math.sqrt(n)) } return R_('number|Complex<number>', n => { if (isNaN(n)) return NaN if (n >= 0) return Math.sqrt(n) return cplx(0, Math.sqrt(neg(n))) }) } ``` and a short and sweet implementation like one from negate: ``` {bigint: () => b => -b} ``` would become ``` {bigint: () => R_('bigint', b => -b)} ``` (The symbol `R_` was proposed above to be as short and sweet as possible since idealy the function name would be '->' ;-). But 'yields' is another possibility, as well as a straight structural notation like: ``` {bigint: () => ['bigint', b => -b]} ``` The advantage of using a function symbol like `R_` is that there's an opportunity to put more code there if it ends up being helpful; the advantage of a purely structural notation is that there is no symbol to import and it is very compact. Note the exampe of `sqrt` was chosen above to show that the return type would need to come after the dependencies, not on the outer level, because the return type can depend on the dependencies as supplied. (In fact, in some sense the latter part of the sqrt implementation should be something like: ``` const complexReturn = returnType(cplx) return R_('number|' + complexReturn, n => { ... implementation ... }) ``` but that's now getting a bit cumbersome... maybe R_ could be made cleverer so that you could just write: ``` return R_(['number', cplx], n => { ... imp ... } ``` That now seems like the way to go. Besides allowing a typescript declaration generator as per https://github.com/josdejong/typed-function/issues/123, return type annotation could be used to * provide type-checking of expressions in the parser * guide simplification of expressions (and thereby eliminate the assumptions option) * provide a compose function: `math.compose(math.sqrt, math.negate)` which produces a function that computes sqrt(-x) but only performs type dispatch _once_, when negate gets the argument, but arranges to call the proper implementation of sqrt on the result without having to typecheck. This compose operation could be used to provide the semantics of expressions, nearly eliminating the typechecking that would go on in evaluating an expression. So it seems well worth the effort.
glen added the
enhancement
label 2022-08-10 15:01:22 +00:00
Author
Owner

Resolved in #53

Resolved in #53
glen closed this issue 2022-08-30 19:37:19 +00:00
Sign in to join this conversation.
No Milestone
No project
No Assignees
1 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: glen/pocomath#52
No description provided.