Continuations library for Java

RIFE is an open source Java web application framework which allows web applications to benefit from first-class continuations. Now, RIFE's pure Java continuation engine, which uses Java bytecode manipulation to implement continuations, has been extracted into a standalone Java library:

Announcing RIFE/Continuations, pure Java continuations for everyone

The immediate motivation for doing this was to achieve support for continuations in the WebWork application framework, which now supports them it its latest beta.

Along similar lines, there's also the Apache Jakarta project, Commons Javaflow.

From a language design perspective, it's interesting to see features like this being introduced without modifying the language implementation. Other examples of continuations being added to languages, as in Stackless Python and the Javascript implementation Rhino with Continuations, have been done by forking the language implementation, which of course isn't as practical in Java's case.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

There is no need for continuations for web apps.

What was needed is a library that works on AJAX principles (html, javascript, xml) that allows building web apps ala Swing apps (with full model-view-controller support) which are 100% Java.

Thank God some people were sane enough to do it: echo2.

Back button support.

All Web frameworks must be evaluated with regard to, among other things, how they handle the back button, how they relate UI state to application state, how they scale (is the continuation persistable, for example) and failures (browser crashes, server crashes).

echo2, from what I can make out from the docs, doesn't handle back buttons well. It's design rationale is that a a web app is no different from a regular event-driven GUI app and that a "back" button doesn't make sense in a complex UI. This is an unfortunate stance because web users are used to hitting back and forward.

Back to our regularly scheduled LtU topics ...

That does not mean Echo2 is not in the right track.

The back button can be modelled like a GET request to go back, and therefore handled appropriately.

Web apps are indeed no different than regular event-driven GUI apps.

True to a Point

axilmar: The back button can be modelled like a GET request to go back, and therefore handled appropriately.

They certainly can be, but then you need a framework for ensuring that the state of request variables, session variables, etc. is consistent across these requests. You end up doing what happens automagically in continuations.

axilmar: Web apps are indeed no different than regular event-driven GUI apps.

That's true, in the sense that if the only tool you have is a hammer, everything looks like a nail. :-) Continuations just happen to be a more powerful tool than your average hammer.

Nope.

They certainly can be, but then you need a framework for ensuring that the state of request variables, session variables, etc. is consistent across these requests. You end up doing what happens automagically in continuations.

The concept of 'back' as in 'roll back' is totally wrong. Many people have got that wrong. 'Back' means 'the previous' action, not the previous state. Therefore, there is no need to keep the last state around, after a commit.

Wizards are good examples of such functionality: they have a 'back' button, which takes the wizard to the previous screen, so as that the user can see the previous action. Once the user clicks 'finish' though, the wizard can't go back, and there is no meaning in going back.

Web apps work on the same principle: the user can go back and edit his/her data, with the server not needing to know anything. When the user presses 'submit' on the final screen, then the 'back' button should drive the user to the main screen or to the wizard that contains the commited data.

That's true, in the sense that if the only tool you have is a hammer, everything looks like a nail. :-) Continuations just happen to be a more powerful tool than your average hammer.

...or if you don't really need a hammer, but you admire hammers so much that you find them applicable to everything.

I don't think Paul was talkin

I don't think Paul was talking about using continuations to implement rollback, though they can give you that cheaply. In fact, they're wonderful for implementing event-driven applications - the effect is similar to a stateful event loop.

I don't think that making thi

I don't think that making things more complicated that they need to be is to the benefit of a programming team. Programmers have to worry about a lot of things, and throwing the option of using continuations only makes things unnecesserily more complex.

Basically, I never saw the need for continuations in any of the web apps I have participated in. The simple model-view-controller pattern is more than enough. And request that is out of sync (i.e. not currently in service) is ignored with an error message.

Managing complexity

Programmers have to worry about a lot of things, and throwing the option of using continuations only makes things unnecesserily more complex.

Have you actually tried using a framework that integrates continuation support? I'm guessing the answer is "no". The point is that using continuations should reduce the complexity of the application at the source code level. The framework should make the use of continuations essentially transparent, and the continuations themselves simplify the program structure.

Basically, I never saw the need for continuations in any of the web apps I have participated in.

I trust that you're not suggesting that your experience should be extrapolated to all web apps, everywhere.

I think one thing that's often missed in discussions of web apps is that in-house web applications are often much more complex than the various public web apps, where a shopping-cart workflow is about as complex as it gets.

With in-house and/or B2B financial services applications, for example, workflows can get much more complex, spanning multiple users and possibly multiple companies, with a single process unfolding over a period of days, weeks, or even months. Continuations can simplify the development and maintenance of such applications.

I'm skeptical. I used to b

I'm skeptical.

I used to believe in using continuations for Web apps, but now I'm not so hot on this idea.

1) From a language design perspective, what about unwind-protect? err, try/finally?

2) Do continuations interact with the GC properly? (Think allocation of stack frames etc. Think caching of requests.) This used to be a huge problem in Borges (Ruby) and Seaside (Smalltalk).

3) A sobering opinion by Phillip J. Eby: (link)

Continuations are to web programming as functional decomposition is to GUIs. That is, an extremely bad idea at the UI level.

Continuations as a concept are useful for maintaining the state machine of a long-term process like a workflow, bug report, that sort of thing. They suck at short-term GUI interactions for the simple reason that they are very attractive to linear-thinking programmers. Thus, the linear-thinking programmer tries to turn the user into a peripheral, rather than leaving the user in control of the UI. This leads to UI that sucks you-know-what.

If you think you need a linear web UI, there's a very strong chance you have no idea how to write a decent web UI.

I think it's very sad that Java is catching on with continuations-based Web development. It's a blind end.

note also..

that "sobering opinion" was in 2003.

It depends how you use continuations

It depends how you use continuations, there are web frameworks that require you to do everything with them. This imho is not a good idea since, as the quote says, it makes web applications very linear.

However, RIFE (and its continuations engine) doesn't impose continuations upon you. There are actually more situations where they don't make sense than situations where they are useful. I refer to these as "islands of functionality". They are typically long linear processes that for user-friendlyness should be split up into several steps (wizards, registration, questionnaire, competence tests, ...). Writing this with continuations makes it much more convenient and natural than having to worry about the state manually.

Also, as Anton says below, the extracted engine can be used in any context, not just web applications. There are already people that are using it to optimize the performance of asynchronous event processing systems, for instance.

What Is This Linearity You Speak Of?

I don't get these comments about continuations making applications very linear. At least I think they are based on an incorrect understanding. As I understand the point, people are arguing that continuation based systems give you only a single continuation for the 'next action', so the argument goes that web sites with branching structure can't be represented. The send/suspend/dispatch paper shows that this is not the case. You can embed lots of continuations in your pages, and the dispatch happens automagically, just like it with a single continuation.

Linearity

Indeed - Seaside apps *are* basically event driven. But how would you feel about a GUI framework that was so relentlessly event driven that it didn't allow you, ever, to show a modal dialog box? All continuations do is let you bring in some modality where it's appropriate; a good framework still lets you be modeless/event driven most of the time. (Though I do think that the general feel of web apps is further to the modal side than a typical desktop app).

Motivation

First, it should be noted that the main announcement above is that the RIFE continuations engine is now a standalone library, which means that it can be used outside the web context.

Second, dismissing first-class continuations for use in web applications may be short-sighted. I'll respond to an excerpt from Eby's "sobering opinion" quoted above:

"Continuations as a concept are useful for maintaining the state machine of a long-term process like a workflow, bug report, that sort of thing."

Continuations as a concept exist everywhere in every program in all Turing-complete languages, and being more aware of their existence is an unequivocally good thing. In that context, the first-class continuations are useful if only as a tool to help people think more explicitly about control flow in their applications.

For example, the REST style offers some very specific ideas about how control flow should be dealt with in web applications. Those ideas can be quite concisely expressed in terms of continuations, and in fact the extant treatments of REST could benefit from the additional rigor and precision which this would introduce.

As a system feature, first-class continuations can indeed by useful for maintaining the state machine of a multi-step process, or workflow. It doesn't even need to be a particularly long-term one. The concerns often raised about scalability etc. don't apply to all applications, and given the appropriate technical implementation of continuations, need not apply at all (more on this below).

Eby makes the criticism that use of first-class continuations engenders an undesirable linear approach. I think there's some merit to that concern. However, as with any powerful language or system feature, we have to work with it and gain experience with it to find out how it fits best, which patterns work well and which don't. We're still in the very early stages of that.

Also, the current state of first-class continuations, even in languages like Scheme where they originated, is fairly primitive. Some of the criticisms against certain applications of first-class continuations are actually criticisms of the extant implementations: they tend to be monolithic, not supporting delimited continuations, often not supporting serialization of continuations, etc.

More advanced support for continuations could allow the development of web applications with all of the good scalability characteristics of a REST application, and even with the same underlying mechanisms, while still allowing the high-level code to be decoupled from the application's page structure, in a way that existing REST programs cannot easily achieve.

To achieve such capabilities, we have to start somewhere. The existing first-class continuation capabilities are a great start.

Could you elaborate a bit?

Those ideas can be quite concisely expressed in terms of continuations

I'm afraid I don't understand what you're talking about. REST says, be stateless; when you can't, use explicit state, but as little of it as possible. Continuations are about implicit, unbounded, expiring state.

while still allowing the high-level code to be decoupled from the application's page structure

This isn't a goal. Web app design is interface design. It's all about "page structure", and you need to control it as tightly as you can, not abstract away from it.

More advanced support for continuations

If you need to e.g. serialize continuations (to migrate user sessions metween machines on a server farm), I'd say you've already lost the main benefit of a PHP or Rails-like "shared-nothing" architecture, and created yourself a bottleneck. Keeping user state in a database, keyed by a session key, provides mostly the same advantages; but it's more transparent and has unlimited scalability.

Elaboration

REST says, be stateless; when you can't, use explicit state, but as little of it as possible. Continuations are about implicit, unbounded, expiring state.

You're thinking of a specific kind of first-class continuation, applied in a particular way. I was referring at that point to the theoretical concept of continuations, which are an abstraction of control flow in general, which applies as much to REST as to any other kind of program.

In web-based REST systems, URLs and HTML forms are typically embedded in web pages in order to support the required flow between pages. These are in fact serializations of continuations. By themselves, they're not complete: they depend on the resource that they point to. The "actual" continuation is a combination of a URL and any related form fields on a web page, and the resource which the URL references. Those two things together specify a possible next step in some program, i.e., a continuation.

From this perspective, what REST says is that you should arrange programs so that their continuations can be serialized efficiently, to a form like the one described above. To achieve this, any state which the application needs, beyond the parameters of a single request, is typically persisted as a "resource", and given an address, so that it can be referred to by a URL. This means that REST's control flow can be characterized by how it arranges continuations: rather than treating continuations as temporary entities which exist for the life of a "session", it tends to create longer-term resources, and uses those as part of its representation of continuations.

The difference between these REST-style continuations and the Scheme-style first-class continuations that you're balking at is a difference of implementation detail. It is certainly possible to abstract away from the implementation details of REST-style continuations, so that at the application development level, you're not dealing directly with their low-level representation as URLs, and not necessarily constrained by the application's page structure.

[allowing the high-level code to be decoupled from the application's page structure] isn't a goal. Web app design is interface design. It's all about "page structure", and you need to control it as tightly as you can, not abstract away from it.

This argument is always made when new abstractions are introduced. Assembly language programmers made it, and C programmers make it about e.g. memory management. There are always some cases where the proponents of the lower-level approaches have a point, but in a majority of cases, the more abstract approach wins as soon as the tools are capable of doing automatically what previously was done manually. The arguments against greater abstraction tend to seem particularly convincing early on, when the abstract approaches seem to only be applicable in a small number of cases because of the state of the technology. Continuations are currently at that stage.

I've addressed some of these issues in these slides. Ignore the code example towards the end of those slides, though, it doesn't have much relevance to the more general points I'm addressing above.

If you need to e.g. serialize continuations (to migrate user sessions metween machines on a server farm), I'd say you've already lost the main benefit of a PHP or Rails-like "shared-nothing" architecture, and created yourself a bottleneck.

Even that "shared-nothing" architecture serializes continuations, as resources and the URLs which point to them. Application frameworks could do a much better job of allowing you to exploit an understanding of that principle.

Keeping user state in a database, keyed by a session key, provides mostly the same advantages; but it's more transparent and has unlimited scalability.

The session key could just as easily be a continuation key, and the transparency win is on the side of systems which can properly abstract continuations. Combined with a good management framework, continuations don't require the same kind of explicit management as explicit sessions, they simply arise naturally out of the structure of your program.

Thank you... but I'm biting again

This makes sense. But I'm going to argue a bit more :-)

If I understood you right, what you're saying is: whenever we model something as a state machine, we're using continuations without realizing it. We can reformulate it in an even stronger way: whenever we use control flow, we're using continuations without realizing it.

This is certainly true, in a theoretical sort of way. The question is, what should we be using explicitly? Continuations or state machines?

To be useful, a "higher-level", "automatic" abstraction should have fewer problems than the "lower-level" one. With continuations vs state machines on the Web, this doesn't seem to be the case. Continuations have lots of problems that just don't arise in REST setups.

1) Back button depth. (Typically limited to about 10 or 20.)

2) Session forking. (What if you use a "transparent" Seaside thingy like in_transaction, and fork in the middle? REST rightly penalizes you for even attempting this stuff.)

3) Copy-n-paste of URLs. Sending a URL to a friend. Linking into the middle of an application.

4) Stateless load balancing. (The "solution", serializing general continuations, is a Hard Problem and has its own host of issues, e.g. security.)

5) Garbage collection in the presence of cached continuations. (Unsolved to date, I think.)

6) Human-readable URLs.

7) Cache-unfriendliness (typically, an unique continuation ID is generated on each request). This means GET-unfriendliness - continuation-based apps tend to overuse POST.

...And lots of other stuff I didn't think of right now.

Note that most of this is "outside" problems. You've made your application simpler internally, but now the rest of the world (users, server machines, infrastructure machines) has to handle all those problems, instead of you. A very bad sign to me.

This reminds me of "the RPC debate". The advocates of RPC (CORBA, SOAP) and Web continuations frequently use the same argument: "this tool of ours helps us handle a lot of interface complexity transparently".

We should be eliminating interface complexity, not handling it transparently.

RESTful Seaside URLs

It turns out that your problems (2) and (3) above aren't wholly insurmountable - you can combine RESTfulness with continuation-based session state:

http://www.cincomsmalltalk.com/userblogs/avi/blogView?entry=3256899497

Yes

Of course they aren't unsurmountable. But, as Avi writes, it's extra work.

I've actually worked on a continuation-based Web framework (Borges, a backport of Seaside to Ruby, by Eric Hodel). Then became disappointed... and then discovered REST and didn't look back.

Extra work

Sure, it's extra work, but it's no more extra work than using REST would have been. What Seaside does is give you a choice: it lets you avoid that work when you don't need bookmarkable URLs, vs. forcing you to do the extra work the whole time. At a minimum, this lets you iterate faster in early stages of development (when I can't imagine that bookmarking matters). Though in practice I've never found a web app where more than about 10% of its state needed to be preserved by a bookmark.

A use case

I just thought a bit... You have a point. There is a feasible use-case for continuations in Web development.

Imagine a "wizard" (linear interaction), with the possibility to jump back to any step. The whole wizard should be just one HTML page (one hidden form, which the wizard fills out). The intermediate steps of the wizard are implemented with AJAX, continuation keys and the whole caboodle. When the wizard (transaction) finishes, the hidden form is submitted and we leave the page.

This way, the issues with back button depth, session forking, readable URLs etc. just go away - the user doesn't see them.

However, I doubt that server-side continuations will be of much use here :-) And this interface violates the user's "back button instinct"... Definitely something to think about, though.

Potentials

Continuations as a concept exist everywhere in every program in all Turing-complete languages, and being more aware of their existence is an unequivocally good thing.

Indeed. Once you begin to see the continuations explicitly, relatively simple tricks tend to make much of the pain of web applications disappear. (One example of a really simple trick is the Infernal Device.) Better continuation support would expose more opportunities; judging the potential by the state of current tools alone is not very useful.

Another continuations library for Java

As my Google Summer of Code project I developed this summer a library to support both tail-calls and continuations in the Java programing language (and, more generally, in the JVM) - the JauVM.

Put simply, it's a JVM bytecode interpreter in Java that, by managing its own stack, adds the support for those two features. For a more thorough explanation of the idea, read my initial proposal. It's still "fresh" but it "works".

I'm a long time reader, but I had never posted. With all the discussion around spammers, I refrained from posting this until today. I do hope this bit of "self-promotion" isn't deemed inapropriate and against the LtU etiquette. Really sorry if it is.

Cool, and appropriate

Sounds like a cool project!

Re appropriateness, it's certainly appropriate to mention your own work when it's relevant to the discussion.

Status

What's the status of these projects? How mature is RIFE? Which project is easiest to hack/tweak/customize?

Goals?

Depending on your goals, my favorite way of doing continuations on the JVM is still with SISC Scheme.

The continuations used by Cocoon seem mature enough, but they're implemented via a JVM Javascript implementation (Rhino with continuations). Cocoon itself requires that you use its web framework, or you could presumably use Rhino on its own.

So for something actually in Java, as opposed to some other language on the JVM, or for something that's not restricted to a particular web framework, RIFE may be one of your only choices. (It looks like Commons Javaflow isn't released...)

So for something actually in

So for something actually in Java...

Yes, that's the goal (but we may change it, seeing as this is for a course project). What I want the student to work on is (adding support for) delimited continuations...

Perhaps stating the obvious,

Perhaps stating the obvious, I would think that's easier to do by either modifying SISC or RhinoWithContinuations, both of which are fairly tractable language implementations in Java. (SISC has a nice paper about its implementation, too.) I don't know anything about how RIFE's bytecode manipulation works, so I don't know what would be involved with that, but I'd guess it would be hairier.

Thanks

I was thinking about SISC, never checked Rhino though.