Ulrik's Blog
@ulrikdamm

Railroad programming in Swift

Optionals in Swift are generally pretty great, and the language provides some really neat syntax for handling them, like the if-let and the ?? operator. Still, sometimes you run into situations where there is just no good way of handling them, and you’ll have to resort to force-unwrapping with !.

Let’s take an example of this. Say we have an app that syncs profiles with an HTTP API. We define these two handy functions for converting a profileId to a URL string, and another for creating an NSURL from a String). The first will fail if the profileId is invalid; the second, if the generated URL string is invalid.

func pathForProfile(profileId : Int) -> String? {

if profileId < 0 {

return nil

} else {

return "api.com/profile/\(profileId)"

}

}


func toUrl(string : String) -> NSURL? {

return NSURL(string: string)

}

Now, we’ll make a ProfileRequest class, which takes a profile ID, and sets its profileURL property:

class ProfileRequest {

let profileURL : NSURL

init?(profileId : Int) {

if let urlString = pathForProfile(profileId) {

if let url = toUrl(urlString) {

profileURL = url

}

}

// Error

}

}

In the initializer we get the path string, and if that is not nil, get the url from that, and if that also is not nil, we set our property. If the profileURL couldn’t be set, we want to return nil from the initializer, since the object will be invalid.

There’s just one problem here: There is no clear place to return nil. The only solutions I see here is either having a return nil as an else to each of the if-statements, which is not very pretty; or you can move the entire thing to another function; or you can make profileURL an implicitly unwrapped optional, and check if it’s nil in the end of the initializer.

Luckily, there is a way to do this nicely, and of course, we look to our functional friends, who have thought deeply about these things for many years.

To get to this nice solution, we’ll have to look at the problem in a different, more high-level way. A higher-level way of looking at programs is often flow-charts, but I find it more friendly to look at it like a railroad. The train is what is currently executing; the tracks are all the possible paths the train can take.

=🚋===== pathForProfile ====== toUrl ======> set profileURL

              ||                ||

             fail              fail

Here is the initializer above represented as a railroad track. First, pathForProfile is executed. That might fail; or it might succeed, in which case it executes toUrl. This might also fail, but if that also succeeds, profileURL can be set. There’s two points of failure, and right now, those doesn’t do anything. If we want to handle these, the railroad should look more like this:

===== pathForProfile ====== toUrl ======> set profileURL

  ||                ||

          fail ============ fail ======> return nil

Now, the failure cases actually leads somewhere, instead of just stopping. Now, how can we translate that back to code? If just there was an easy way to describe this exact railroad directly in the code. Well, these is, in fact, a way to just that. Introducing the bind function:

func bind<T, U>(optional : T?, f : T -> U?) -> U? {

switch optional {

case .Some(let v):

return f(v)

case .None:

return nil

}

}

(quick note: I’m not an expert on functional languages, and I’ve seen this function called both bind and flatMap, so I chose bind, since it sounds more friendly than flatMap. I’m not sure if there’s actually a difference between those two.)

Bind is a very simple function, which takes two parameters: first an optional; second a function with signature T -> U?, where T is the type of the value in the optional. The idea behind this function is pretty simple: You give a function to the optional, and it is only run if the optional is non-nil. If the optional had a non-nil value, the function is executed, and the result from that is returned by bind. If the optional’s value is nil, bind just returns nil. Let’s see that in action:

let i : Int? = 1

let a = bind(i) { i in "\(i)" }

a // { Some "1" }

Here, we have an Optional i, with the value of 1. We then bind it with a closure, that takes an (non-optional) Int, and returns it as a string. Since i is non-nil, the closure is called, and we get back the sting “1”, wrapped in an optional.

let i : Int? = nil

let a = bind(i) { i in "\(i)" }

a // nil

Here, we have set i to nil. Now, when we call bind, since the value is nil, the closure is not called, and bind just returns nil.

The bind function represents one part of the railroad. It takes one value as an input, and if that value is valid, the railroads goes on to the next step (in this case, our to-string closure). If the input value isn’t valid, it goes to the failure-track by returning nil.

===== bind =====> closure

       ||        

      fail =====>   nil

The utility of this will be more evident when we combine some more steps with this method. Let’s take our profile-url example from before, and rewrite it with bind. Remember, before, it looked like this:

if let urlString = pathForProfile(profileId) {

if let url = toUrl(urlString) {

profilePath = url

}

}

With bind, it looks like this:

if let url = bind(bind(profileId, pathForProfile), toUrl) {

// url is valid

} else {

// url is nil

}

If we first look at the innermost bind, it takes the profileId (which is not an optional, but is automatically upgraded to one) and the pathForProfile function. If you provide a non-optional as the first argument, it will basically act as a function call. Since the value is non-nil, it will be provided to the pathForProfile function. The pathForProfile will then return an optional string, which is then the first parameter to the second bind function, which, on non-nil input, will call toUrl. If pathForProfile returns nil, toUrl won’t be called, and the whole expression will return nil, and run the else block. If it did, however, return a non-nil value, that will be fed to the toUrl function. That function can then either return nil or a non-nil NSURL, and in the latter case, our railroad will finish on the success path, and we will end out with a valid url.

This might be a bit hard to see with the code like this, and frankly, it doesn’t look much like our railroad illustration from before. Don’t worry, thought, because this is a very legitimate place for custom operators:

infix operator => {

associativity left

}


func =><T, U>(lhs : Optional<T>, rhs : T -> U?) -> U? {

return bind(lhs, rhs)

}

I’ve chosen the operator => to represent bind, because it kinda looks like a railroad track. Let’s write that piece of code again with this:

if let url = (profileId => pathForProfile => toUrl) {

// url is valid

} else {

// url is nil

}

Now we can actually see our railroad in the code. We start with a value of profileId, which goes to pathForProfile, which goes to toUrl, which returns a value. If anything goes wrong somewhere on the railroad, the train will to go the failure path, which is the else clause.

(In case you’re confused over the syntax: writing just ‘pathForProfile’ without applying any parameters will return a reference to the function, rather than calling it. profileId => pathForProfile just calls bind with profileId and pathForProfile, such that if profileId is non-nil, it will be provided as the input for pathForProfile, which will call the function. The return value from pathForProfile then becomes the first parameter in the next bind function.)

I think this is a very simple and powerful way of dealing with things that might go wrong, and can actually lead to more readable code than the old style of calling functions with parameters, even when there were no error handling. The good thing about this is also that you cannot forget to do error handling: it has been taken care of for you.

Let’s look at some more examples:

label.text = (dataStore => getProfile(12) => getName) ?? "No name"

Here, we’re setting the text of a label to the name of profile 12. It might be that profile 12 doesn’t have a name; it might be that profile 12 doesn’t exist; and it might be that the dataStore doesn’t exist. All these failures are handled here. If anything here returns nil, it will go to the ?? operator, and return “No name”. (in this example, getProfile is a curried function, like func getProfile(profileId : Int)(dataStore : DataStore) -> Profile)

Sometimes you have a simple method call, like removeConstraint(layoutConstraint), where your parameter suddenly is an optional, and since the methods parameter isn't, you need to do the whole if-let dance:

if let layoutConstraint = layoutConstraint {

removeConstraint(layoutConstraint)

}

With the bind operator, this becomes as simple as

layoutConstraint => removeConstraint

You can even use methods on instances by using this syntax:

let p = Profile()

let r = (p.topPost() => Profile.getCommentToPost(p)) ?? "No comment"

(getCommentToPost is just a method on Profile. Profile.getCommentToPost(p) will return a Post -> String? function)

I hope you see the value in this, because this is a really powerful thing, and can make code a lot more readable, while at the same time handling errors for you transparently. Speaking of errors, what if you’re actually interested in why something went wrong, and want more information than just a nil? Let’s make a version of bind, which, instead of working on Optionals, works on a value/error result enum.

Bind with Result

If you’re not familiar with the concept of the Result enum, it is a very nice way of handling results from an operation that might fail. How this is typically done in Objective-C is by returning a value and passing an NSError double-pointer through a parameter. This is no good, and kind of a hack. Luckily, in Swift, we have something much better. Well, almost, that is. What we would like to do is to define an enum like this:

enum Result<T> {

case Ok(T)

case Error(NSError)

}

This this, we can make a failable operation function signature like this:

func readFile(filename : String) -> Result<String>

And when you use this function, you will have to unpack the results like this:

let result = readFile("file.txt")

switch result {

case .Ok(let r):

println("Success: \(r)")

case .Error(let error):

println("Failed: \(error)")

}

This approach to error handling is really nice, because it forces you to handle the error. If you want the result from the operation, you’ll have to unpack the Result enum, which will force you to handle the error case.

There’s just one problem with this: Right now, if you were do try to define the Result enum, you would get an error, saying that you can’t do that, since the feature hasn’t been implemented in the compiler yet. Bummer. But, there is a way to achieve the same thing: we can redefine our enum using a wrapper object:

class Box<T> {

let value : T

init(value : T) {

self.value = value

}

}


enum Result<T> {

case Ok(Box<T>)

case Error(NSError)

}

By defining the box class, which is just a class that hold a generic value, we can put that as the value of the Ok case, and it will compile. This is a bit of a hack, but it will only be necessary until this feature is implemented in the compiler.

Anyway, now we have a Result enum we can use for failable operations. Now, there is more benefit to this way of error handling than just the example we saw earlier: we can use this to implement our railroad oriented programming. Let’s define a bind function (as the => operator), which works on a Result enum, instead of an optional:

func =><T, U>(lhs : Result<T>, rhs : T -> Result<U>) -> Result<U> {

switch lhs {

case .Some(let box):

return rhs(box.value)

case .Error(let error):

return .Error(error)

}

}

This is the exact same as we did with the optional, just with a Result instead: if the result is an error, we just pass the error along; if the result is an Ok, we will continue the railroad by calling the next function (rhs). Notice that we also have to unpack the value from our Box class.

We will define one more function, just for convenience:

func =><T, U>(lhs : T, rhs : T -> Result<U>) -> Result<U> {

return rhs(lhs)

}

This is the same function, except that it doesn’t take a Result, but just a T, on the lefthand side. This is because with our optional-bind function, a variable could automatically be wrapped in an optional. We can’t do this with a Result, and we don’t want to manually have to wrap a value in a Result enum. this function will just call rhs with lhs, but you will see later why this fits better into the syntax of our railroad.

Let’s look at an example of using this. First, since there are no functions yet that actually uses our Result enum, so we will have to define some. Since these are just for demonstration, I won’t have a proper implementation of these.

func readFile(filename : String) -> Result<String> {

return .Ok(Box(value: "contents of file"))

}


func parseJSON(jsonData : String) -> Result<NSDictionary> {

let dictionary : NSDictionary = ["object": "value"]

return .Ok(Box(value: dictionary))

}

Here’s two functions for reading a file (which just returns “contents of file” right now), and for parsing a json string into an NSDictionary (which just returns ["object": "value"] right now). Let’s parse the contents of a json file:

switch ("filename" => readFile => parseJSON) {

case .Ok(let box): println("result: \(box.value)")

case .Error(let error): println("error: \(error)")

}

The "filename" => readFile => parseJSON is all we need to read the file and parse the json. It doesn’t get more compact and simple than this, and all error handling is already done for us. The result of this expression will be a Result, which we then unwrap. If the readFile fails, that error is returned from the entire expression. If the parseJSON fails, that error is returned from the entire expression. If we want to know what failed, we can look at the NSErrors domain property. You might also use your own error object, in which case you could make the Result enum generic on both the value and the error.

Our railroad for this example looks like this:

===== readFile ====== parseJSON ======> Ok

         ||              ||

        fail ========== fail =========> Error

It has been a bit difficult for me to explain, but I hope that you see how powerful this concept is. We kill two bird with one stone with this: Our code becomes much more compact and readable, and at the same time, we decrease the chance of making a mistake.

This concludes this post on the topic, but that doesn’t mean that there isn’t more to it. One thing that for me quickly came to mind was if these blocks could be run asynchronously. Because, as most programmers know already, async really sucks. In my next post, I will look at using bind with promises, and finally, hopefully, making async programming easy. But no promises there.

More about this topic

Stay up-to-date by following me on Twitter