This might seem like a minor thing, but it ends up being pretty important in practice. E.g. most airline fare comparison sites don't allow you to do multiple searches in parallel; most checkout processes get very unhappy if you use the back button. The basic browsing metaphor -- of a page's state being contained within that page -- is broken by cookies, and it shows even in these toy examples.
(Sometimes breaking that metaphor is ok -- e.g. for persistent logins.)
With the closure-based approach, you don't have to worry about tabs, or the back button, or any other issues caused by badly leaking abstractions.
I think cookies vs closures is mixing apples and oranges. First, if you're using server-side state, the session ID can be stored in a hidden field (post) or URL (get) as Arc does, or the session ID can be stored in a cookie. The cookie behavior can be a bug or a feature depending on what you're doing; for example, you probably don't want two different tabs to have two different shopping carts and two sets of user information. Session ID in the URL has the disadvantages of ugly URLs ("sessionid=line noise"), lack of persistence, difficulty with bookmarking, and SEO negatives. Web frameworks (JSP, ASP) typically support either model.
Second, improving back button behavior is a matter of setting all the right nocache attributes. If you use forms, you're likely to have trouble with back navigation no matter what you do ("The page contains expired POST data").
Finally, I don't see how closures have any impact on the user experience one way or another, since it's just a different way of storing server-side state. The server can be implemented with closures, in-memory state, state backed by a database, or trained pigeons and it shouldn't make any difference to the user. (Modulo performance, reliability, etc of course.)
Am I missing something about how closures solve tabs and all other leaky abstraction issues?
"I think cookies vs closures is mixing apples and oranges"
But that's really the point: even though both styles make either model _possible_, they still encourage radically different approaches -- and this is of course borne out by the stuff people have submitted.
Secondly, on the perceived cookies/closures dichotomy, you reduce things to a question of where one should store the session ID, and that's a totally orthogonal issue.
We basically have three models -- sessions with IDs in cookies, sessions with IDs in URLs, and closures. Cookies give you a single, linear progression of state; session-IDs-in-URLs give you multiple linear progressions; and closures give you a nonlinear tree of progressions.
What do I mean here?
Sessions with IDs in cookies are simple: they're a single global state. Session IDs in the URL are a small bit closer to the closure-based approach, but they really just mean that the flow of state proceeds linearly in several independent threads. It's still a long way from matching the browser metaphor of "a page's state being contained within that page".
With session IDs in the URL, you can't get identical semantics to the closure-based approach unless you do make your sessions immutable, and create a new clone at each juncture. And if you do adopt this "session frame" approach (should that be "stack frame" approach?) you've basically created succumbed to some form of Greenspun's Tenth Rule.
Does this really matter? When state is contained within the page -- that is to say, when the closure-based approach is used -- it makes for much more flexible browsing. To take a tangible example, say you're searching for an airfare. You first search for tickets from SFO to BOS, then on the next page pick dates, and then on the third you (holding down alt) open a new tab investigating the price when you book first-class, while you proceed with economy in your main tab. You go through a few more pages, and you're now at the checkout in both. But you realise that you really need to go a day earlier, and that first-class is too expensive, so you close that tab, and hit back 'till your at the date selection page, and then head back to the checkout.
And it all just works. We humans are inquisitive creatures, and the tree of closures facilitates our natural instinct to poke and then retreat when things don't look right. Desktop app designers have long known this (even the first Macintosh had undo), and though generally far less information-dense, almost any modern desktop app supports virtually-infinite undo. Session-based approaches on the web choke this.
If the airline example sounds a bit contrived, it's because it is -- but at the same time, it's also a lot less chaotic than many people's flows when going through this an airline booking process (I know that for a fact because I spent a while investigating it once...).
The biggest difference between my example and real life is that, in mine, things _work_, whereas in real life, opening the new tab or using the back button would almost certainly completely screw the web-app up.
As heavy internet users, we've grown to accept this crap, because 1) we're used to it and 2) we know it's hard to get right. But it's not inevitable. Browsers can still be made to work as advertised.
This response has been hurried; I should probably make it into properly-written blog post or something.
Another option is to have all necessary information about the current operation in the URL. This is highly scalable, since you don't need to keep track of anything user-specific on the server(s), and the navigation supports branching and back/undo just like you describe.
Strangely enough PG specifically disallow this approach in his competition!
Most web apps need _both_ global session state and URL-based state. As others have pointed out, if you browse a product catalog, you would like to be able to branch into different browser windows or use the back-button. However, when you add an item to the shopping basket, you want it to be a global state change (you want have the same shopping basket in all windows), and you don't want a buy to be undone by clicking back.
Continuations are only an options for handling URL-based state, not for handling global state. And for page state they have some limitations.
For example, if all navigation is handled by continuations, you basically have to store a continuation for every hit indefinitely, since you dont know if the user have bookmarked the URL. If you don't want to store the continuations forever, you should only use them on pages that are not bookmarkable anyway, i.e. pages that are the response to form posts. But then the stated advantages, like the ability to branch and use the back button is moot, since you cannot do that anyway with form responses.
Continuations are really nifty for quick prototypes of web apps, but for production use, I believe they are a leaky abstraction.
I think you're spot on. I just wanted to share a little trick, with regards to:
Second, improving back button behavior is a matter of
setting all the right nocache attributes. If you use forms,
you're likely to have trouble with back navigation no matter
what you do ("The page contains expired POST data").
The key is to use a 302 redirect immediately after a successful post rather than a 200. This makes using the back button take you back to the form, rather than trying to POST it again.
On the other hand, I find the ability to resubmit forms with the back button very useful at times, so I'm not sure this is always the right thing to do. But it's a neat trick.
Well, even that code omits the definition of "said" and "clicked". (The latter is unnecessary as far as I can see.)
I agree that mine isn't precisely by the spec -- you have to press 'enter' to submit the form, and there's an ok button on the last page -- but these are fairly trivial differences in presentation due to the behaviour of the built-in libraries.
(also, minor footnote: I'm assuming that "foo", "said" and "clicked" are instance variables in each example.)