mikeash.com: just this guy, you know?

Posted at 2010-02-05 18:36 | RSS feed (Full text feed) | Blog Index
Next article: Friday Q&A 2010-02-12: Trampolining Blocks with Mutable Code
Previous article: Friday Q&A 2010-01-29: Method Replacement for Fun and Profit
Tags: blocks continuations errorhandling fridayqna objectivec
Friday Q&A 2010-02-05: Error Returns with Continuation Passing Style
by Mike Ash  

The Earth has moved 6.9 degrees around the Sun since my last post, which means it's time for another edition of Friday Q&A. This 6.9-degree segment, Guy English has suggested that I talk about the use of continuation passing style to simplify error returns in Objective-C code.

NSError **
The standard Cocoa convention for signalling errors to a caller is to return the error by way of an extra parameter pointing to a NSError * variable, like so:

    NSError *error;
    NSString *string = [[NSString alloc] initWithContentsOfFile: path encoding: NSUTF8StringEncoding error: &error];
    if(!string)
        // do something with error
This works, but is a bit painful to type. It's also painful to implement, because the code needs to set the error and return nil as two separate steps. The error parameter is also optional, so the method needs to do a NULL check for every error case, like so:
    if(failure)
    {
        if(error)
            *error = [NSError errorWith...];
        return nil;
    }

Other languages handle this quite a bit nicer. For example, the equivalent in Python is:

    string, error = someFunction(...)
And the return is equally easy:
    if failure:
        return None, createErrorObject(...)
This is because Python has built-in support for returning multiple values from a function, whereas Objective-C doesn't. How can we get the Objective-C side to be equally nice?

Exceptions
For languages without support for multiple return values (and often for those that do support it), the common solution for error returns is exceptions. In Objective-C, using this technique would look like this for the caller:

    @try
    {
        NSString *string = [[NSString alloc] init...];
        // use string
    }
    @catch(NSException *exception)
    {
        // handle exception
    }
This is still a bit verbose, however that is often compensated by the fact that multiple calls can be placed in the same @try block, and share the same @catch handler.

The other end of it is nice and simple:

    if(failure)
        @throw [NSException exceptionWith...];
It also allows chaining errors very easily. If you have a method that calls another method and the inner method can error, then allowing the outer method to also error is simply a matter of not catching the exception. With other techniques, you need to explicitly check for errors at every call and propagate them up the call chain.

However, exceptions have their own problems. If a method can return an error and you ignore it, then you just lose out on some diagnostic information. If a method can throw an exception and you fail to catch it, then it can cause inconsistent states and even crashes as the exception is thrown through code that's not written to handle it. Writing exception-safe code requires more thought and care.

These problems aren't insurmountable. Exceptions are often used in other languages for this sort of thing. However, they're not traditionally used in Objective-C, which means that even if you want to use them, it can be tough to deal with all the code out there that isn't aware of them. For routine errors, it will also make it difficult to debug your programs when you end up having a serious error where an exception shouldn't be thrown at all, but is. It's very common to simply break on objc_exception_throw to, for example, figure out just where an exception is being thrown from within some Cocoa calls, but that technique becomes unusable if your application frequently throws exceptions as part of its routine operations.

If you don't want to use exceptions, but want something better than NSError **, what can you do?

Continuation Passing Style
Continuation Passing Style, or CPS, is a style of programming using anonymous functions to replace return statements. Instead of returning a value, a function will take another function as a parameter. Then, when it gets to the point where it would have returned a value, it calls the passed-in function with the value as a parameter instead. In Objective-C, we now have anonymous functions in the form of blocks, so CPS can be achieved using blocks.

Here's an example of how CPS looks. This is the standard-style code:

    NSString *string = [obj stringWhatever];
    // use string
And here it is converted to CPS:
    [obj stringWhatever: ^(NSString *string) {
        // use string
    }];
CPS has many different uses, such as providing a convenient interface for asynchronous operations. Here, however, I want to use CPS to take advantage of a convenient fact: although Objective-C methods can only return one value, Objective-C blocks can trivially take two parameters.

Thus, the annoying error return-by-reference style can be converted into a cleaner CPS version, like this:

    [NSString stringWithContentsOfFile: path encoding: NSUTF8StringEncoding continuation: ^(NSString *string, NSError *error) {
        if(error)
            // handle error
        else
            // use string
    }];
This is nice and straightforward. As a bonus, Xcode will automatically complete the basic outline of the block for you if you hit return while on the argument placeholder.

On the other side it's pretty simple to deal with as well:

    + (void)stringWithContentsOfFile: (NSString *)path encoding: (NSStringEncoding)encoding continuation: (void (^)(NSString *, NSError *))continuation
    {
        // create string from path
        
        if(failure)
            continuation(nil, [NSError errorWith...]);
        else
            continuation(string, nil);
    }
Handing errors "up the stack" is pretty easy too. Imagine a method which does its own error reporting using this CPS technique, and which calls that NSString method. If it errors, it just wants to report the error up the chain:
    - (void)computeObject: (id)obj continuation: (void (^)(id, NSError *))continuation
    {
    [NSString stringWithContentsOfFile: path encoding: NSUTF8StringEncoding continuation: ^(NSString *string, NSError *error) {
        if(error)
            continuation(nil, error);
        else
        {
            ...continue processing with string...
            id result = ...;
            continuation(result, nil);
        }
    }];

Two-Block Variant
Since the first action of the continuation is almost certain to be to check and do something different if an error occurred, it would be reasonable to split up the continuation into two parts. This moves the check into the called method (which probably needs to have such a check anyway) and simplifies the caller. You pass an error block and a normal block, and the method will then call the appropriate one. This variant would look like this:

    [NSString stringWithContentsOfFile: path encoding: NSUTF8StringEncoding
        errorHandler: ^(NSError *error) {
            // handle error
        }
        continuation: ^(NSString *string) {
            // use string
        }];
This style also simplifies handing errors "up the stack", because the caller's error handler can just be passed straight in as the error handler for the next level:
    - (void)computeObject: (id)obj errorHandler: (void (^)(NSError *))errorHandler continuation: (void (^)(id))continuation
    {
        [NSString stringWithContentsOfFile: path encoding: NSUTF8StringEncoding errorHandler: errorHandler continuation: ^(NSString *string) {
            // continue processing with string
            continuation(string);
        }];
    }
Interfacing Between CPS and Normal Code
Using CPS for error returns isn't all roses. A problem comes at the interface between CPS code and normal-style code. In other words, where you have a method which needs to return a value, but which calls CPS methods. The problem arises because using the return statement inside a block returns a value from the block, not from the enclosing function:
    - (NSString *)contentsOfFile: (NSString *)path
    {
        [NSString stringWithContentsOfFile: path encoding: NSUTF8StringEncoding
            errorHandler: ^(NSError *error) {
                [NSApp reportError: error];
            }
            continuation: ^(NSString *string) {
                return string; // fails! returns string from the block, not the method!
            }];
    }
In order to get around this problem, you need to move the value outside of the continuation using a __block-qualified local variable, then return from within the main function body:
    - (NSString *)contentsOfFile: (NSString *)path
    {
        __block NSString *returnValue = nil;
        [NSString stringWithContentsOfFile: path encoding: NSUTF8StringEncoding
            errorHandler: ^(NSError *error) {
                [NSApp reportError: error];
            }
            continuation: ^(NSString *string) {
                // string is retained here in case there are any inner autorelease pools
                // in the NSString method that's calling this continuation
                // retaining it will keep it alive even if there is one and it is popped
                // the retain is balanced by an autorelease in the method body
                returnValue = [string retain];
            }];
        return [returnValue autorelease];
    }
This is not a huge inconvenience, but it is mildly annoying, and certainly removes some of the elegance of using CPS here.

A bigger downside is that, well, Cocoa doesn't do CPS error returns, it does return-by-reference. It's not too hard to write adapter methods:

    typedef void (^ErrorHandler)(NSError *error);
    
    @implementation NSString (CPSErrors)
    
    + (void)stringWithContentsOfFile: (NSString *)path encoding: (NSStringEncoding)encoding errorHandler: (ErrorHandler)errorHandler continuation: (void (^)(NSString *string)continuation
    {
        NSError *error;
        NSString *string = [self stringWithContentsOfFile: path encoding: encoding error: &error];
        if(string)
            continuation(string);
        else if(errorHandler)
            errorHandler(error);
    }
    
    @end
But obviously the annoyance of having to write adapters makes for a big hit to the niceness of using CPS for error handling.

Conclusion
The addition of blocks to Objective-C enables some completely new ways of doing things, including a new way to deal with errors. The practicality of this approach remains to be seen, but it certainly does produce nicer-looking code than the traditional Cocoa way.

That's it for this Friday Q&A. Come back in 5.56×1015 periods of the radiation corresponding to the transition between the two hyperfine levels of the ground state of the caesium 133 atom for the next exciting installment. Friday Q&A is driven by user submissions, so if you have a topic you would like to see covered here, send it in!

Did you enjoy this article? I'm selling whole books full of them! Volumes II and III are now out! They're available as ePub, PDF, print, and on iBooks and Kindle. Click here for more information.

Comments:

Man, you got me there until "This is nice and straightforward".
Thought you might want to know that you've got quite a few spots where you spell continuation as "contituation", which may not have been your intent.
Yeesh, I have no idea how I was able to make that typo so many times. Fixed, and thanks!
Thanks, good topic. A couple of mental comparisons this dislodged:

First, the two-callback-based system that Twisted Deferreds use ("callback" and "errback"): http://twistedmatrix.com/documents/9.0.0/api/twisted.internet.defer.Deferred.html) is an asynchronous implementation of what you describe, but it makes sense to see it used synchronously too.

NSWorkspace does have a couple of methods that provide an NSError to the callback (recycleURLs: and duplicateURLs:), as in your first example, so there is some possibility that this style will be used more in future.

- (void)recycleURLs:(NSArray *)URLs completionHandler:(void (^)(NSDictionary *newURLs, NSError *error))handler AVAILABLE_MAC_OS_X_VERSION_10_6_AND_LATER;

- (void)duplicateURLs:(NSArray *)URLs completionHandler:(void (^)(NSDictionary *newURLs, NSError *error))handler AVAILABLE_MAC_OS_X_VERSION_10_6_AND_LATER;

Finally, your comment about return not returning from the containing function made me laugh—I was just reading a blog entry where someone was trying to address Smalltalk having the opposite problem. Of course, Smalltalk being Smalltalk, it was actually possible to work around this by adding a method: http://www.cincomsmalltalk.com/userblogs/mls/blogView?entry=3441567670.
well, it may be a nice way of doing it but overusing blocks makes the code unreadable.

I'd rather type 10 lines than have unreadable code. :(
Nicholas Riley: The NSWorkspace methods aren't exact analogs, because they're using CPS for asynchronicity, and if you're writing asynchronous code then you have to use a block, function pointer, object/selector pair, etc. callback, and pass the results into it. Using blocks for synchronous continuations is fairly different, although this does show that Apple isn't afraid of this sort of thing.

Interesting note about Smalltalk. What happens if you use the ^ return operator after the enclosing scope has already returned?

jrk: One man's elegance is another man's unreadable, I suppose. Care to elaborate on why you find this to be unreadable?
If I'm not mistaken Apple and/or convention has dictated that exceptions are meant for programming/-er errors, not for any old error. Another reason not to use exceptions to return useful runtime information.
Well, Apple conventions don't get everything right, and as long as you don't let exceptions leak into code you don't control, then it doesn't matter what Apple wants you to do, the question is simply whether they work well for you. Of course, Apple's guidelines and the fact that the community mostly follows them means that almost any third-party code you work with won't be exception-safe, which will make using them routinely considerably harder.
Does the blocks syntax require the open-brace to be on the same line as the ^(...) ? If not, it might be slightly more readable without the mixed brace style.
No, you can put it wherever you want. Like (most of) the rest of C, all whitespace is equivalent. My preference is to leave it on the same line, but I couldn't tell you why that's different from everywhere else....
How the CPS style affects readability of a method which has several method calls which may return NSError values and which are handled within the method, i.e. the method doesn't pass NSError up to the caller? Imagine the following method written with standard error conventions:


- (BOOL)doSomething
{
    NSError *error = nil;
    id obj1 = [obj objectWhateverWithError:&error];
    if(obj1 == nil)
    {
        [self _handleError:error]
        return NO;
    }
    
    id obj2 = [obj1 objectWhateverWithError:&error];
    if(obj2 == nil)
    {
        [self _handleError:error]
        return NO;
    }
    
    id obj3 = [obj2 objectWhateverWithError:&error];
    if(obj3 == nil)
    {
        [self _handleError:error]
        return NO;
    }
    return YES;
}


How this method would look like in CPS style?
An interesting question. This is a basic translation:

- (BOOL)doSomething
{
    __block BOOL returnValue = NO;
    
    [obj objectWhateverWithErrorHandler: ^(NSError *) {
            [self _handleError: error];
        }
        continuation: ^(id obj1) {
            [obj1 objectWhateverWithErrorHandler: ^(NSError *) {
                [self _handleError: error];
            }
            continuation: ^(id obj2) {
            [obj2 objectWhateverWithErrorHandler: ^(NSError *) {
                    [self _handleError: error];
                }
                continuation: ^(id obj3) {
                    returnValue = YES;
                }];
            }];
        }];
    return returnValue;
}


The nesting does get a bit ugly, but I don't find it to be too horrible. In reality, I would probably rewrite it as a series of smaller methods:

- (BOOL)doSomething
{
    __block BOOL returnValue = NO;
    [obj objectWhateverWithErrorHandler: ^(NSError *) {
            [self _handleError: error];
        }
        continuation: ^(id obj1) {
            returnValue = [self doSomethingWithObj1: obj1];
        }];
    return returnValue;
}


If you want to get fancy, take advantage of the fact that your three error handlers are all identical, and define that handler up front so you just pass a variable in to each one:

- (BOOL)doSomething
{
    __block BOOL returnValue = NO;
    
    void (^errorHandler)(NSError *) = ^(NSError *) { [self _handleError: error]; };
    
    [obj objectWhateverWithErrorHandler: errorHandler continuation: ^(id obj1) {
        [obj1 objectWhateverWithErrorHandler: errorHandler continuation: ^(id obj2) {
            [obj2 objectWhateverWithErrorHandler: errorHandler continuation: ^(id obj3) {
                returnValue = YES;
            }];
        }];
    }];
    return returnValue;
}


I find this to look really nice, but I don't know how likely it would be to find a situation where you could use it in real code.
Your last example looks like the most readable, but you're correct that it probably isn't the most realistic example.

I really like the idea of CPS, but I think (at least with current syntax) it decreases the readability of the code. In first example, the indentation level (which is actually conditional nesting level in this case) grows from 1 to 4, in second example from 1 to 2 and requires the developer to break the method into smaller ones, which may or may not be desirable (if the original method is already performing a single semantic thing and all method calls are on the same abstraction level, it may not make sense to break it down).
Coming from Smalltalk, i've also enjoyed using error-blocks as arguments a lot. In Smalltalk querying a dictionary for a key that's not in the dictionary raises an error. So the common way of getting values out of a dictionary is not #objectForKey: but more along the lines of #objectForKey:ifAbsent:. The second parameter is a block that is evaluated when the key is absent.

I'm not sure if that already addresses your question about the return operator before, but you can write a method #street like this in a class Person:

Person>>street
   | address |
    address := addresses at: self ifAbsent:[^nil].
    ^address street

If there is no address for the given person, it'll execute the error-block. In that block is a return, so the #street method returns nil. If an address was found, the #at:ifAbsent: would return this address and would not evaluate its error-block.
Thus the method does only return after "address street" is evaluated.
My question about the return operator was more like this (forgive my presumably-poor syntax, since I've never really written Smalltalk):

Person>>something
    ^[^nil]


In other words, you return the block from the enclosing method, then call it somewhere later, but it still contains a ^ statement. I know this probably makes no sense to do intentionally, but I'm wondering what happens if you try it anyway.

Given the discussion about Smalltalk, I'm finding myself wishing that Objective-C blocks had the same semantic where the block's value is simply the value of its last expression. Having to write return everywhere is annoying and sometimes confusing.
I really like this idea a lot, but there is one thing I don't really see.... The need for the second part, i.e. the continuation block. If you have this signature:

stringWithContentsOfFile:encoding:errorHandler:continuation:

I don't really see the advantage to having the continuation block at all! If I don't error give me back the string and let my program continue its normal flow, but let the block handle any errors.

How does that second block help me out?

Thanks for the great posts.
An interesting question. The best I can come up with is that returning the string directly forces you to have an if statement, which is avoided with the pure CPS approach. E.g.:

NSString *myString = [obj ...message];
if(myString)
{
    // it's good
}

Versus:

[obj ...message: ^(NSString *myString) {
    // it's good
}];

The rest is basically the same amount of code, so you save the if statement. You also lose the convenience of having code flow return directly to your method body....
in your example, if you evaluate the block later, this will raise an error that the context cannot return.

As for the implicit return of blocks. That would be great, but that just doesn't work in the c-based Objective-C, where you explicitly need to declare the return type.
Raising an error makes sense. Thanks for the explanation.

Objective-C blocks are already doing return type inference. Declaring the return type of a block is possible but not mandatory. This inference could be used for implicit returns as well. Really, implicit return is just a bit of syntactic sugar in this case. It's just like putting the return keyword right before the last statement in the block.
I've written this little code to add SmallTalkish methods to ObjC using blocks. The last part of the example shows a way to use continuations:

http://gist.github.com/239363
One potential pitfall I can see with doing heavy CPS style is that, IIRC, GCC does not do tail-call optimization, which means that each new continuation introduces a new stack frame. Stacks are pretty large these days so you're not likely to run into a problem, but the possibility is still there. Of course, if you're using LLVM I believe that does tail-call optimization now.
I disagree about your statement that Python has the ability to return multiple values.

Python is able to return a list or tuple. Automatic unpacking allows you to unpack the array in multiple values:

aTuple = ("value1", "value2", "value3", "value4")
value1, *value2and3, value4 = aTuple

# Is the same as
aTuple = "value1", "value2", "value3", "value4"
value1, *value2and3, value4 = aTuple

# Is the same as
def aFunction():
  return "value1", "value2", "value3", "value4"

value1, *value2and3, value4 = aFunction()
Python can't return multiple values in the same way that C can't pass function parameters by reference. It's true, but existing language features combine to give you functionality that's indistinguishable. Python effectively has multiple value return, even if it's actually just tuple packing and unpacking, the same way that C effectively has pass-by-reference, even though it's actually just passing a pointer by value.
Using blocks in this way could make some kind of interesting Obj-C flavor of javascript. jQuery aggressively uses this technique:


$.ajax({
   type: "POST",
   url: "some.php",
   data: "name=John&location=Boston",
   success: function(msg){
     alert( "yay!" );
   },
   error: function(xr, s, e) {
     alert( "aww :(" );
   }
 });


http://api.jquery.com/jQuery.ajax/
That is neat! I didn't know JQuery had that. Would translate almost perfectly into ObjC with blocks.
Interesting ideas... One of the downsides of CPS in Haskell and other FP languages is the nesting that inevitably occurs, as displayed by your example in the contents. Monadic notation does a lot to fix this (do ...) - wonder if the same pattern can be applied here in order to come up with an even cleaner notation? Need to define >>, >>= equivalents, and maybe a #define for the do notation...
I like this a lot but now that Swift converts NSErrors to throws it's hard to adopt :(

https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/AdoptingCocoaDesignPatterns.html

If the last non-block parameter of an Objective-C method is of type NSError **, Swift replaces it with the throws keyword, to indicate that the method can throw an error. If the Objective-C method’s error parameter is also its first parameter, Swift attempts to simplify the method name further, by removing the “WithError” or “AndReturnError” suffix, if present, from the first part of the selector. If another method is declared with the resulting selector, the method name is not changed.

If an error producing Objective-C method returns a BOOL value to indicate the success or failure of a method call, Swift changes the return type of the function to Void. Similarly, if an error producing Objective-C method returns a nil value to indicate the failure of a method call, Swift changes the return type of the function to a nonoptional type.
I think you can do it even better with Swift errors, since you can write a single generic adapter function instead of writing a bunch of adapter methods. For example:

func handle<T>(_ call: @autoclosure () throws -> T, errorHandler: (Error) -> Void, continuation: (T) -> Void) {
    do {
        let value = try call()
        continuation(value)
    } catch {
        errorHandler(error)
    }
}

enum E: Error {
    case e
}

func f() throws -> String {
    return "Hello, world"
}

func g() throws -> String {
    throw E.e
}

handle(try f(), errorHandler: {
    print("error: \($0)")
}, continuation: {
    print("success: \($0)")
})
handle(try g(), errorHandler: {
    print("error: \($0)")
}, continuation: {
    print("success: \($0)")
})


However, I think Swift's error handling system is good enough to obsolete this whole idea.

Comments RSS feed for this page

Add your thoughts, post a comment:

Spam and off-topic posts will be deleted without notice. Culprits may be publicly humiliated at my sole discretion.

Name:
The Answer to the Ultimate Question of Life, the Universe, and Everything?
Comment:
Formatting: <i> <b> <blockquote> <code>.
NOTE: Due to an increase in spam, URLs are forbidden! Please provide search terms or fragment your URLs so they don't look like URLs.
Code syntax highlighting thanks to Pygments.
Hosted at DigitalOcean.