Hacker News new | past | comments | ask | show | jobs | submit login

Continuations are strangely underused, as they enables writing long-living processes in a simple way, without having to keep them in running in a thread, or even in memory.

Then a real programming language can replace all "business processing" crap languages. Let's say you write a framework that escapes to a continuation whenever the "process" is waiting for Futures or Promises to complete, and returns the thread to a pool.

Depending on what you are waiting for, such as webhooks, asynch events/messages, or with some work even outstanding network requests ( if it's meaningful ), it would be possible to serialize the continuation to storage, potentially restore it on another machine, weeks later.

With some bookkeeping, it would be possible to also store what Futures have been completed, and what the continuation is currently waiting for, so it would be possible to keep track of the progress of the long running process, and use that information to show a nice status report.

I have implemented interpreters that support both full and delimited continuations. They are underused for many very good reasons. A continuation makes an implicit state machine. Implicit means anonymous, which means harder to talk about, which means harder to reason about. But there are technical problems too.

Continuation based sessions, as used in fringe web frameworks such as Seaside or even Arc's library that powers Hacker News, is a really problematic approach. Reifying a slice of call stack in to a continuation and serializing it introduces all sorts of problems for resource management and the user experience, especially in the presence of change.

For example, let's say you deploy a new version of your code: What happens to all the in-progress sessions? Do you let old code keep running? Do you force users to start over? Compare to "Edit and Continue" in a Java/C#/Smalltalk codebase where you can change a method and all instances get the new behavior, vs if you change a function that constructs another function: All the old closures stick around with the old code.

Let's say a user leaves a browser tab open for a while: How long do you wait before invalidating the session and free its resources? What impact does that have on the user experience? Hacker News has mostly eliminated its reliance on continuations to remedy these sorts of problems.

Speaking of resources: What resources does a continuation hold on to? If you serialize a continuation, do you keep all file handles open? Do you perform static analysis to know a priori what data should be garbage collected or excluded from the continuation as "semantic garbage"? This problem is double bad when laziness is involved. Consider paginating through a database cursor.

What happens when a continuation must be forked? Consider using a continuation-id to resume an interaction with a web app: Does opening a new tab duplicate any held resources by the continuation? Do your external resources even support cloning operations or are they thread safe? Compare to unix "fork" and file handles vs shared memory.

> For example, let's say you deploy a new version of your code: What happens to all the in-progress sessions?

This is a problem inherent to long-running processes, no matter the implementation. We had the same problem in our business process engine using jbpm (implemented as an interpreter serializing state to database every step).

Ultimately you have to decide if you bind functions/subroutines/subprocesses/whatever you call them early (so when you call them later and there's a new version - they still use old version), or late - so they are always calling the newest version. And you have to adjust your coding assumptions and the way you update your software to new version basing on that decision.

Neither way is always the correct one.

Came to say the exact same thing, all the problems mentioned aren't unique to continuations. I'm not saying this makes a positive argument to use coroutines everywhere, but it's not their fault.

As ajuc mentioned, long-running processes and early/late bound is always a thing. Same with database migrations. Heck, even clients accessing a versioned api (foo.com/api/v1/bar) vs not has the same consideration.

> How long do you wait before invalidating the session and free its resources?

Same decision has to be made for explicit session data like login state and pagination cursors in eg. Facebook's graph API. Continuations are larger and therefore this question may have more weight.

> What resources does a continuation hold on to?

Anything reachable (static and/or dynamic analysis). And if you run your program in a monad (or other restricted, pure manner), then you can control when a function can suspend, like not in the middle of paging a database.

> Does opening a new tab duplicate any held resources by the continuation?

If you live in an immutable world, you get duplication for free with structural sharing. Same goes for an OS loading a shared library once and giving the same pages to multiple programs.

I do agree that having a blob of stuff serialized isn't very nice. Same goes for closures as well. The example I always compare in my head is a functional representation of a set vs. any other data structure.

I toyed around with an interpreter that would let you reflect a closure (same could be applied to continuations) to a program, which is akin to a residual program in partial evaluation. Then you could do whatever you want with the source of the closure/continuation. For the early/late bound question above, you could analyze the reflected continuation and decide exactly which bits you want to rebind before continuing the continuation.

If only Brendan Eich had ended up "doing Scheme" in Netscape as he was originally recruited to! (https://news.ycombinator.com/item?id=2786720)

Javascript survived (and eventually caught on) not just because it was the "only game in town" for the web, but also because it was familiar.

If Eich indeed had produced a scheme, then it would have been superseded and replaced by another algol-derived language by browser makers soon -- and users would have flocked to that.

If familiarity mattered that much, why wasn't HTML itself replaced by something with curly braces, or CSS by something procedural?

Because HTML wasn't a programming language.

Template languages were like that (e.g. SGML) and worse.

Besides, unlike JavaScript it wasn't the product of a single vendor, but a standard, and the basis upon which the web stood from the beginning.

We are doing exactly this in my current company. There is a quite nice language called Orc for this. A site can do IO, we can serialize the state and return to it weeks later when we have a triggering event.

Continuations have a context which takes up space in memory. That context keeps a reference on an environment (or chain of environments) containing all the resources which must be there when the continuation is invoked.

I would say that cotinuations In the sense of call/cc are overused. You have to look hard for a problem where undelimited continuations are in any way better than delimited ones.

For the vast majority of cases, delimited continuations are faster, use less memory (and makes garbage Collection easier) and are generally easier to reason about.

Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact