archives

Strongly Typed Coroutines (minor question)

I'm considering "asymmetric full coroutines" and their operators as defined in this before LTU described paper Revisiting Coroutines. At least recent versions of Racket support these style of coroutines I'm told (documentation lags), but in a latently typed language.

In this model, a coroutine supports operators:

  • c = create p
  • r = resume c s
  • s = yield r

Furthermore, when the coroutine function exits "normally", it is then "dead".

Thus coroutines are as much, or more, "pipes", with values "s" fed by resume and consumed by yield, as they are "generators" just returning values "r" from yield (as in Python, as I understand its limited generator mechanism).

Just as important, as a "stackful" construct, the yield call may occur anywhere on the call stack, in any called routine - a desirable property.

Thus, my inquiry is how to type the yield call (r -> a) in a manner expected by the coroutines' creator and caller (hence the "asymmetric" description of these coroutines).

One possibility is to wrap the coroutine in some kind of parametric class type:

  class Coro s r is ... yield: r -> s; ... end 

The coroutine object might be passed down the call chain eventually to any routine wishing to yield, now via:

  s = co.yield r;

This seems quite burdensome, requiring design of routines explicitly for coroutine use. An attraction of coroutines is a flexible, more-or-less ad hoc inversion of control mechanism.

Another, more complicated option, is to percolate yield types from routines up through callers, in the manner of Java checked exceptions, restricting the universe of routines callable from a coroutine, eliminating potential calls to yield that break type constraints.

This latter method only affects routines that are known to call yield in their subroutine calls, either directly or via a passed HOF (objects with methods calling yield, etc.).

A second question is how to distinguish values returned via yield from the final regular coroutine return indicating the death of the coroutine. Is it customary to wrap every yield'ed value in Some r and then return None upon coroutine death? The same question might be asked regarding any indication of the exhaustion of values sent to the coroutine via resume c s.

Or is some potentially more memory efficient, if stylized, mechanism typically employed? FWIW, the use of exception handling language facilities for this kind of "out of band" control flow offends my sensibilities. Dynamic control via continuations, CL's lexical block label dynamic exit special form, or even Icon's fail mechanism all present themselves more appropriate.

Yes, a rather narrow pair of question for LTU, but I hope it will interest some folks here, and as always, any and all help greatly appreciated.

- Scott