Blog

Functional Snippet #14: Flattening and Mapping Optionals

In previous snippets, we have seen the bind function on arrays (also called flatMap sometimes). We can define the same type of function on optionals. Let’s consider the following two dictionaries:

let populations = ["Paris": 2243, "Madrid": 3216, "Amsterdam": 881, "Berlin": 3397]
let capitals = ["France": "Paris", "Spain": "Madrid", 
               "The Netherlands": "Amsterdam", "Sweden": "Stockholm"]

If we want to lookup a value, we could do the following:

func populationOfCapital(country: String) -> Int? {
    if let capital = capitals[country] {
        return populations[capital]
    }
    return nil
}

When we try to write this function with a map on optionals, we only need a single return statement, but end up with a nested optional:

func populationOfCapital2(country: String) -> Int?? {
    return capitals[country].map { populations[$0] }
}

For the cases where you are chaining multiple functions that return an optional, flatMap can come in very handy:

func flatMap<A,B>(x: A?, y: A -> B?) -> B? {
    if let value = x {
        return y(value)
    }
    return nil
}

Now, we can rewrite our populationOfCapital using a single return and with the right result type:

func populationOfCapital3(country: String) -> Int? {
    return flatMap(capitals[country]) { populations[$0] }
}

Alternatively, we could use the >>= operator, just like we did for lists, and write it in between the functions. The operator scales a bit better when you have multiple flatMap calls in one statement, because it removes the need for parentheses.

func populationOfCapital4(country: String) -> Int? {
    return capitals[country] >>= { populations[$0] }
}

Stay up-to-date with our newsletter or follow us on Twitter.

Back to the Blog

recent posts