rain-1 / rain-1.github.io

GNU General Public License v3.0
4 stars 0 forks source link

scheme-continuations #5

Open utterances-bot opened 5 years ago

utterances-bot commented 5 years ago

Are first class continuations a language-design dead end? | rain-1.github.io

https://rain-1.github.io/scheme-continuations.html

rain-1 commented 5 years ago

p.s. more detailed technical notes on continuations here https://github.com/rain-1/continuations-study-group/wiki

manuel commented 5 years ago

This paper contains an implementation of MPDC in terms of CALL/CC https://www.cs.indiana.edu/~dyb/pubs/monadicDC.pdf

mbrock commented 4 years ago

I wonder if another way to look at it is: how successful is the Schemish/Lispish vision that the language provides a minimal but powerful kernel supporting a great range of user-defined extensions, and where applications are written in higher-level "DSL"-type jargons implemented with metaprogramming and abstract primitives like delimited continuations (and some low-level reflectional futzing).

Another more concrete question: Should the JavaScript committee have added delimited continuations to the language instead of adding generators and async/await? I guess the immediate argument against is that it might immediately deprecate all existing implementations because they're not optimized for capturing continuations.

Hmm, is there a big difference between implementing async/await and, say, prompt/control? I really don't know. I guess two differences are: (1) that async/await are lexically determined, and (2) that a promise is defined to only resolve once. So those might allow an easier efficient implementation? Like you can do a local CPS transform only pivoting on the await clauses. The control flow is more static, lexical. But with general continuation capturing, any nested (late-bound) function call can do a continuation capture. It's like "control flow with dynamic scoping." And the continuations are restartable functions so you can only optimize them away in specific cases.

I'm currently working on a Lisp interpreter in JavaScript that tries to be as inefficient as possible (bluntly speaking) in order to instead be maximally reflective, serializable, and metaprogrammable. I've decided to use an implementation style that I believe might be novel only because of a lack of research into deliberately inefficient language evaluation techniques. So what I'm doing is something like a direct implementation of a small-step semantics relation, with continuations represented by AST-like nested structures. Lambdas are just ASTs with scope pointers. Control operators get translated on the fly into continuation structures that contain syntax trees. This seems like the most naive way to implement a small-step evaluator and makes multiprompt delimited continuation capturing easy. You get a debugger for free, basically.

Why go to all this trouble? I really want to be able to do more metaprogramming than is currently possible with JavaScript, particularly relating to asynchronous control flow. For example, there's now a rift between built-in control structures—for loops with break, switch statements, etc—and anything you define using higher-order functions. You can't feasibly make a custom pattern matching operator because you would need to pass the case handlers as functions, so you won't be able to break a loop from within a case. And it seems hard to make a nicely usable library that e.g. implements preemptible fibers, because you can't define your own version of await. And so on.

And so it seems reasonable to implement the framework of multiprompt delimited continuations and then build on that. Because hey, I may well want to use nondeterministic choice operators, I bet that can be really convenient, and I bet you can find very nice use cases for concurrent actors doing backtracking search, etc etc.

I really only believe that their uses are to implement more human-understandable control operators - and I kind of find it hard to believe that there are any left that we don’t already know about.

Maybe "structured concurrency" is an example of a control structure that's very conceptually coherent and yet somehow forgotten and not part of any mainstream programming language as far as I know. I am also always a bit stunned by how little interest there has been in Common Lisp's "restartable conditions." You can support such things really well with MPDCs.

One reason MPDC operators haven't caught on more might be the scary prevalence of different formulations and semantics, which kept me away from learning for quite some time: I always got confused by whether I should try to understand shift/reset, prompt/control, or any of the 7 other cryptic dualisms. Continuations in general seem to have a reputation problem similar to Haskell's monad problem, and is it coincidental that a monad is basically just a data type to describe a CPS program?

Very interesting topic... Sorry for the rambling quality of my comment!

manuel commented 4 years ago

Delimited control has absolutely caught on IMO. It's available in multiple major Scheme implementations (Guile, Racket, probably others), it's getting added to Java (https://wiki.openjdk.java.net/display/loom/Main) and other languages have coroutines with often very similar semantics to DC (http://lambda-the-ultimate.org/node/4349).

atengberg commented 3 months ago

@mbrock tbimho this is the best kind of rambling re: context of computer science. Thanks!