I thought I was done with CPS
I stumbled across a couple blog posts the other day talking about applying continuation-passing style (CPS) in Objective-C. The underlying technology that makes it all possible was Apple’s (as-yet unstandardized) introduction of blocks into the C language.
The thrust of the post was about how error handling is handled in Objective-C. Generally methods will either return a valid value, or some special invalid value (usually nil) and fill an error object passed by reference to the function. This pattern generally appears in code as:
NSError * error;
int errorHappened = [anObject someMethodWithError:&error];
if(errorHappened) {
// do something with error
}
The argument those couple posts put forward was that error handling could be done more “elegantly” using blocks for passing one or more continuations to a function - that way, the function itself could handle errors internally by dispatching either a regular continuation or an error-handler continuation.
There are a couple problems that arise technically with this implementation right away, and those are touched upon in the original post:
- Developers have to use
__block
qualified variables to do C-style returns right - Return by reference makes it necessary to use “adapter” methods in some cases
To me, these seem like pedantic, esoteric arguments against a coding style that has several more fundamental things wrong with it. First off (and most trivially), the syntax gets really ugly really fast. Take, for example, what happens when we convert an error argument to a continuation (example again stolen from the original blog post): the method header turns from
+ (NSObject *)doSomething:(id)anArg error:(NSError**)error;
into
+ (void)doSomething:(id)anArg
continuation:(void (^)(NSObject *, NSError *))continuation
I know Xcode’s autocompletion is pretty strong, but that still seems like quite a bit to deal with. Note additionally the change in return type of methods that use continuations rather than returns to handle program flow: this means any libraries that choose one over the other are going to have conflicting styles for constructing programs.
This leads nicely into another problem with CPS in ObjC: stylistic issues (in the sense of what’s “right” for the language). Objective-C tries to make heavy use of objects, and provides facilities for autoboxing primitives (when necessary), class-based collections such as arrays, and other such niceties. Furthermore, it encourages an object-oriented approach to program control and structure; mixing C and Objective-C freely within a file isn’t commonly done. Given all that, it seems more than a little backwards to suddenly rely on a C-level construction, even with the use of adapter methods to tidy up the interface a bit.
Finally, there’s the matter of thinking about programs in CPS to begin with. Objective-C is, at its heart, an object-oriented language; it’s called “Object”ive-C, after all. Continuations are just as fundamentally a functional construct, most commonly seen in Lisp-like languages (and to a certain extent in Python). Mixing the two together just feels strange - the program would be defeating several established OO conventions, and writing code that utilizes a CPS flow would feel unnatural to almost any established Objective-C developer. That’s not to say that language paradigms can’t or shouldn’t change, but think about the Java equivalent: Clojure got spun off as a separate language, rather than try to integrate functional concepts into the main Java language.
Long story short: are continuations feasible in Objective-C? Yes, entirely. Are they reasonable? Perhaps not - the Objective-C world already has several established patterns that can just as effectively accomplish what continuations provide, in a more natural, established way for object-oriented developers.
And who knows - maybe Apple can provide us with a Functional-C before too long.