Ulrik's Blog
@ulrikdamm

Binds and promises with Forbind

Say we want to write an app to display the user's IP address. Very simple, we just make a network request to a service that sends our IP back, and displays it to the user. But, before we start coding, let’s see exactly what we’re going to do by making a good old flowchart.

It’s pretty simple. We will create a network request to “http://ip.jsontest.com”. This operation might fail (we might have entered an invalid URL), which should end the whole process. Next, we send the request. This is obviously also something that might fail, since there might not be a working connection. Then we parse the response as JSON, which also might fail, if the returned value is not a valid JSON document, and lastly we parse the IP string from the JSON response. In the end, we can end up in two places: either something went wrong, or we have our IP address.

Alright, with the formalities out of the way, let’s write some code. This should be pretty simple to do.

func getIp(completion : (String?, NSError?) -> Void) {

let endpoint = "http://ip.jsontest.com"

if let url = NSURL(string: endpoint) {

let request = NSURLRequest(URL: url)

NSURLConnection.sendAsynchronousRequest(request, queue: .mainQueue()) {

response, data, error in

if let error = error {

completion(nil, error)

} else {

var jsonError : NSError?

let json = NSJSONSerialization.JSONObjectWithData(data,

options: nil, error: &jsonError) as? NSDictionary

if let jsonError = jsonError {

completion(nil, error)

} else {

let ip = json?["ip"] as? String

completion(ip, nil)

}

}

}

}

}

There, job’s done. We can test it and see that it works.

Except, there’s something bothering me about this. This code is kind of ugly. It doesn’t strike me as good code. So what’s wrong with it? Well, first of all, there’s a lot of it. And there’s a lot of it which is just annoying distractions like nil checking and error handling. To illustrate this, I’ve tried to highlight the important parts of the code:

The white parts here is what is corresponding to our flowchart. These are the parts of the code, where we are actually telling the computer to perform some action, the rest is just necessities.

And, not only is the code not very pretty, concise, or readable, it’s also filled with small bugs, which can be hard to catch. There are four bugs hidden in this code, try to see if you can find them all.

Found them all? Check the list here:

1: data is an implicitly unwrapped optional, and might be nil, which will make the code crash. 2: If there’s a JSON parsing error, it returns the wrong error object. 3: If the JSON doesn’t have an “ip” key, it will return with neither an error nor result. 4: The completion block is not called if the NSURL object couldn’t be made.

Such a trivial piece of code, and yet buggy and hard to read. We must be able to do something better than this. Well, allow me to show how this code could have looked.

func getIp() -> ResultPromise<String> {

let endpoint = "http://ip.jsontest.com"

let request = endpoint

=> { NSURL(string: $0) }

=> { NSURLRequest(URL: $0) }

let response = request

=> NSURLConnection.sendRequest(.mainQueue())

=> { response, data in data }

let json = response

=> NSJSONSerialization.toJSON(options: nil)

=> { $0 as? NSDictionary }

let result = json

=> { data in data["ip"] as? String }

return result

}

This is the exact same code, but using my Swift library, Forbind. Forbind defined a bind operator, which takes a value and applies it to a function, like a function call. The difference being, that both error handling and asynchronous calls are taken care of, completely transparently. It allows you to write code as a series of commands, without having to deal with nil checking, error handling and completion blocks. If you take a look at the code, there’s not a single if-statement, or a single completion block call. Our getIp() function is also more straight-forward, just starting with the endpoint string, transforming it, and in the end, returning the result.

So how does this work? Well, it’s actually pretty simple. The core of the library is the bind operator: =>.

The bind operator (=>)

As pviously mentioned, the bind operator takes a value, and supplies it to a function, not much unlike a function call. You could call these two equivalent:

function(data)

data => function

So what’s the big deal? Well, what if your function parameter is an optional value? Now you’d have to do the if-let dance. Well, not if you’re binding it, since the bind operator will automatically unwrap the value, before it’s sent to the function. If the optional has a value, that value will be supplied to the function, and the result of it will be returned. If the optional does not have a value, the whole expssion will quit early, and return nil. Here’s an example of it:

func1 : A -> B?

func2 : B -> C

let c = (func1(a) => func2)

// c : C?

In this, func1 returns an optional B, but func2 takes a non-optional B. No worries, when we bind the two values together, it will bind the value inside of the optional, rather than the optional itself. We’re compositing the two functions to create a combined function, which has the type A -> C?. So why optional C as the result? Because, if func1 returns nil, the whole expssion will be nil. If it doesn’t, the value will be carried to the next function, which will return a C.

So, this is nice, eliminating a lot of if-lets. But what about error handling? What if one of the functions also can return an NSError? Well, we have a type in the library for this:

The result type (Result)

The result type in Forbind is a very simple enum, which has a success (Result.Ok) and failure case (Result.Error). It’s like an optional, just where the nil case is accompanied by an NSError. A function that might fail will return a value of Result, instead of just T, and you can unwrap a result with a switch statement:

switch mightFailFunc(a) {

case .Ok(let box):

// handle box.value

case .Error(let e):

// handle error

}

(Notice that because of Swift compiler limitations, we have to return the ok value in a box object.)

So, if we take our bind example from before, but instead of returning an optional, func1 returns a result type, it will look like this:

func1 : A -> Result<B>

func2 : B -> C

let c = (func1(a) => func2)

// c : Result<C>

As you might notice, we have actually not changed the code at all. Only change is that func1 returns Result<\B> rather than B?, and in the end, the result is Result rather than C?. But the bind is the exact same. And this is one of the key points of the library: You shouldn’t worry about if something returns an optional or an error, or just a regular value. You just bind the expssions together, and in the end, the resulting value will have the correct type.

But what if we bind multiple things together that mixes use of optionals and results? Well, let’s try:

func1 : A -> B?

func2 : B -> Result<C>

func3 : C -> D

let d = (func1(a) => func2 => func3)

// d : Result<D>

In the end, our resulting type is Result<D>. This is because this is the most expressive type we need for these statements. Forbind is smart, in that it automatically finds the needed type for the returned value. If func1 returns a nil, the value of the whole expression will be an NSError in the dk.ufd.Forbind domain, with the error message that something in the expression was nil.

I hope that by now, you’ve seen the idea of this: To chain expressions together in a way, so that a value is fed into one end, transformed, and the resulting value is returned in the other end, and if anything goes wrong during the process, the whole chain quits, and returns the error, instead of the result. I think this is a very clear and expressive way of writing code, it reduces complexity, and lowers the chance of edge-case-bugs.

But there’s still something missing from this. What about asynchronous calls, like network requests? Well, don’t worry, that’s covered as well.

The promise type (Promise<T>)

A promise (or a future) is simply put a value, which just might not be there yet. Like an optional is a wrapper for a value, which might not be there, a promise is a wrapper for a value, which is there soon. The powerful thing about promises, rather than completion blocks, is that we can begin to transform and pass on these values, even before they’re loaded. We can, say, tell the computer that we want to parse some binary data as JSON, even though the binary data itself is still waiting to get downloaded.

Just as you would return a Result<T> from a function, that might fail, you will just return a Promise<T> from a function, which loads the data asynchronously.

When you get a promise, you can retrieve the value in the promise with the getValue function:

promise.getValue { value in

// use value

}

Let’s try to use an async function in the same bind example:

func1 : A -> Promise<B>

func2 : B -> C

let c = (func1(a) => func2)

// c : Promise<C>

As you would expect, the code still doesn’t change. The bind operator handles async calls completely transparently, just like failable functions. But what happens now if we try to bind an async function and a failable function together?

func1 : A -> Promise<B>

func2 : B -> Result<C>

let c = (func1(a) => func2)

// c : ResultPromise<C>

We see that the return type is something new. ResultPromise is a failable promise: an async value, which might be an error. Forbind is smart enough to see that the resulting expression is both async and failable, since it is composed of individual functions which is one or more of these. There’s also an OptionalPromise type.

We’ve now taken a look at the bind operator, the Result type, and the Promise type (and ResultPromise/OptionalPromise), and this is actually all we need to know to write our IP example from before.

The forbind library also comes with another library called ForbindExtensions, which defines extensions for common functions, like NSJSONSerialization and NSURLConnection, to use Result and Promise instead of NSErrorPointer and completion blocks. Some of the functions uses currying to split parameters up into some which is just configuration for the call, and which is the data passed into a call. For example, NSURLConnection.sendRequest takes two things: a queue to process the result on, and the url request. These two are different in that the queue is a kind of configuration for the function, while the url request is the input to the function. Therefore, these two are defined in two different parameter-lists, so that it can be used like this:

urlRequest => NSURLConnection.sendRequest(.mainQueue())

You should now be able to understand the IP example code, and be able to write your own functional, expressive code using Forbind!

The combine operator (++)

There is just one more thing. There’s an untacked problem in this: What if you want to bind to a function which takes multiple values? And what if it takes multiple values, which all might be optional, failable, or even worse, async values? Well, there is a solution for that. It’s called the combine operator, and it looks like this: ++. You use it by combining two values together, before binding them to a function.

Let’s take an example: You want to write a Twitter client that shows the mentions between two Twitter users. You do this by loading both their timelines, combining them into one, and then filters by only the tweets where they mention each other. This is what it would look like:

func loadTimeline(user : String) -> ResultPromise<Timeline> { ... }

func mergeTimelines(user1 : Timeline, user2 : Timeline) -> Timeline { ... }


let user1 = loadTimeline("@lars_loekke")

let user2 = loadTimeline("@helle_thorning")


let merged = (user1 ++ user2) => mergeTimelines

In this case, the combine operator is used on two values of ResultPromise<Timeline>. It will unwrap both values, and call the mergeTimeslines function with the two unwrapped values. If the unwrapping of one or both of them fails, it returns an error early, instead of calling the function. And of course, it also works with any combination of optionals, Result types and Promise types.

The end

So that’s it, that’s a quick introduction to my Forbind library. It’s pretty simple, but I think it makes coding certain things a whole lot better. I hope you find it as useful (or at least interesting) as I do. If you want to learn more, head over to Forbind on Github. If you have any questions or comments, Tweet at me on @ulrikdamm. And thanks for reading!