Saturday, March 1, 2008

Failed Attempts At Adding Continuations To Rails

My life would be easier if I could get Rails to act like a continuation server.

Constant Proc Hash

First, I did it the easy way.

You pop a Proc into a hash, which is a constant, and you run in production mode. This keeps the constant alive between HTTP requests, which means you can just pull out the Proc and baddabing baddaboom.

I think running in production mode isn't actually necessary with this technique. I kinda got ahead of myself, because I was already thinking of a different way to do it.

Actual Continuation Objects, Stored On A Class

I felt the constant Proc hash really wasn't good enough. It isn't really a continuation server; it's just an "execute arbitrary code later on" server. It required careful, specific manual effort and the whole point of continuations is that you can preserve any and all state without thinking about it. So I tried a different approach. I put the continuation object inside a cattr_accessor on a class, taking advantage of the fact that that data would persist between HTTP requests, even though instance data in Rails does not. It works pretty much the same way the constant trick does, because a class is a constant, in a sense, but it operates without the ugly environment.rb hackery.

It seemed like a perfect plan. Unfortunately, it acted kinda weird:

I wrote two controllers which added objects to a continuation cattr_accessor. One added strings, the other added continuations. First I wanted to just verify that the cattr_accessor hack would work. It turns out the hack works perfectly for strings, but behaves oddly with continuations. Specifically, the continuation exists, unless you use call to invoke it, at which point it ceases to exist. In the video's RSpec example, calling call on a continuation appears to make the continuation cease to exist before you even call it. It's like Schrodinger's Fucking Variable.

At this point I should probably point out that continuation servers in Ruby already exist. I should also point out that even though continuation servers are the bee's knees, there are likely to be very few use cases where turning Rails into a continuation server is likely to actually be useful, or indeed even sane, because the memory consumption for Rails apps which used continuations extensively would probably be extraordinary. But I have a use case, as unlikely as that may seem.

However, it seems pretty likely that to get the results I'm after, I'm going to have to use either Iowa or Poor Man's Seaside. Eventually, I got the cattr_accessor hack to work, but that didn't buy me much.

It turns out that calling continuations across threads is pretty much totally impossible, which makes adding continuations to Rails a pretty tall order. (Joe O'Brien actually told me this at Canada On Rails in 2006, but it was still fun to find out first-hand.)