← Back to context

Comment by hencq

14 hours ago

I like the overview given in this Stackoverflow answer [1] (based on an even earlier comment) which classifies different types of continuations:

- Asymmetric or symmetric

- Stackful or stackless

- Delimited or undelimited

- Multi-prompt or single prompt

- Reentrant or non-reentrant

- Clonable or not

Based on that these generators (or semi-coroutines as the article also calls them) seem to be asymmetric, stackful, delimited, single prompt(?), non-reentrant continuations.

[1] - https://stackoverflow.com/questions/62817878/what-are-the-sp...

That's a great overview. Yeah they are asymmetric, Wikipedia says symmetric and asymmetric correspond to coroutines and semicoroutines. They are also stackful and delimited. They are single shot by design, though I could easily make it possible to restart the generator from scratch if needed.

As for single prompt vs multiprompt... I'm not too sure about this one. I have a check to prevent recursion but nesting generators shouldn't be a problem since they keep track of their own callers.

I think lone's generators have composability issues due to the stack separation. For example, calling a generator g2 inside another generator g1 doesn't transparently yield values from g2 to g1's caller. I've been wondering about how to fix this without a Python-like yield from primitive.

  • Very cool stuff and great written article. Lone looks very interesting!

    Are you planning to use your design to support things like exceptions as well? I think that's where that multi-prompt ability becomes important (yielding from a nested coroutine). Racket has prompts and aborts, which is essentially a 'yield to' primitive.

    • Thank you. Proper exception/condition handling is a high priority item on my list. It's something that is long overdue... Lone currently hard exits on literally any error.

      My delimited continuations article also talks about exception handling:

      https://www.matheusmoreira.com/articles/delimited-continuati...

      Throw/catch are just delimited continuations which don't actually capture the continuation. The machinery is already in place. If I copied the control/transfer primitives and removed the continuation code, I'd have a functional exceptions handling mechanism.

      In suppose I can use tagged prompts to catch some exceptions and not others. Place a symbol in the stack delimiter, then the transfer primitive can look for it and skip anything that doesn't match. Untagged prompts just use nil as the tag.