One of the first things that we got to see at the 2014 WWDC with the introduction of Swift was that we could use the map function with the collection types. Let’s focus on Swift’s Array.

The benefit of this pattern is that we can very clearly express the transformation that we’re trying to apply on the list of elements (in this case, doubling their value). Compare this with the imperative approach:

It’s not about solving it in a one-liner vs 3 lines, but with the former concise implementation, there’s a significantly higher signal-to-noise ratio. map allows us to express what we want to achieve, rather than how this is implemented. This eases our ability to reason about code when we read it.

But map doesn’t only make sense on Array. map is a higher-order function that can be implemented on just any container type. That is, any type that, one way or another, wraps one or multiple values inside.

Let’s look at another example: Optional. Optional is a container type that wraps a value, or the absence of it.

The benefit of map in Optional is that it will handle nil values for us. If we’re trying to operate on a value that may be nil, we can use Optional.map to apply those transformations, and end up with nil if the original value was nil, but without having to resort to nested if let to unwrap the optional.

From this we can extrapolate that map, when implemented on different container types, can have slightly different behaviors, depending on the semantics of that type. For example, it only makes sense to transform the value inside an Optional when there’s actually a value inside.

This is the general signature of a map method, when implemented on a Container type, that wraps values of type T:

func map<U>(transformFunction: T -> U) -> Container<U>

Let’s analyze that signature by looking at the types. T is the type of elements in the current container, U will be the type of the elements in the container that will be returned. This allows us to, for example, map an array of strings, to an array of Ints that contains the lengths of each of the Strings in the original array.

We provide a function that takes a T value, and returns a value of type U. map will then use this function to create another Container instance, where the original values are replaced by the ones returned by the transformFunction.

We could define it like this:

enum Result<T> {
case Value(T)
case Error(NSError)
}

This is an implementation of a type known as Either in some programming languages. Only in this case we’re forcing one of the types to be an NSError instead of being generic, since we’re going to use it to report the result of an operation.

Conceptually, Result is very similar to Optional: it wraps a value of an arbitrary type, that may or may not be present. In this case, however, it may additional tell us why the value is not there.

To see an example, let’s implement a function that reads the contents of a file and returns the result as a Result object:

Easy enough. This function will return either an NSData object, or an NSError in case the file can’t be read.

Like we did before, we may want to apply some transformation to the read value. However, like in the case before, we would need to check that we have a value every step of the way, which may result in ugly nested if lets or switch statements. Let’s leverage map like we did before. In this case, we will only want to apply such transformation if we have a value. If we don’t, we can simply pass the same error through.

Imagine that we wanted to read a file with string contents. We would get an NSData, that then we need to transform into a String. Then say that we want to turn it into uppercase:

NSData -> String -> String

We can do this with a series of map transformations (we’ll discuss the implementation of map later):

How would Result.map be implemented? Let’s take a look:

Again, the transformation function f takes a value of type T (in the above example, NSData) and returns a value of type U (String). After calling map, we’ll get a Result<U>(Result<String>) from an initial Result<T> (Result<NSData>). We only call f whenever we start with a value, and we simply return another Result with the same error otherwise.

Functors

We’ve seen what map can do when implemented on a container type, like Optional, Array or Result. To recap, it allows us to get a new container, where the value(s) wrapped inside are transformed according to a function. So what’s a Functor you may ask? A Functor is any type that implements map. That’s the whole story.

Once you know what a functor is, we can talk about some types like Dictionary or even closures, and by saying that they’re functors, you will immediately know of something you can do with them.

Monads

In the earlier example, we used the transformation function to return another value, but what if we wanted to use it to return a new Result object? Put another way, what if the transformation operation that we’re passing to map can fail with an error as well? Let’s look at what the types would look like.

func map<U>(f: T -> U) -> Result<U>

In our example, T is an NSData that we’re converting into U, a Result<String>. So let’s replace that in the signature:

func map(f: NSData -> Result<String>) -> Result<Result<String>>

Notice the nested Results in the return type. This is probably not what we’ll want. But it’s OK. We can implement a function that takes the nested Result, and flattens it into a simpleResult:

This is so common, that you will find this defined in many places as flatMap or flattenMap, which we could implement for Result like this:

And with that, we turned our Result type into a Monad! A Monad is a type of Functor. A type which, along with map, implements a flatMap function (sometimes also known asbind) with a signature similar to the one we’ve seen here. Container types like the ones we presented here are usually Monads, but you will also see that pattern for example in types that encapsulate deferred computation, like Signal or Future.

The words Functor and Monad come from category theory, with which I’m not familiar at all. However, there’s value in having names to refer to these concepts. Computer scientists love to come up with names for things. But it’s those names that allow us to refer to abstract concepts (some extremely abstract, like Monad), and immediately know what we mean (of course, assuming we have the previous knowledge of their meaning). We get the same benefit out of sharing names for things like design patterns (decorator, factory…).

It took me a very long time to assimilate all the ideas in this blog post, so if you’re not familiar with any of this I don’t expect you to finish reading this and immediately understand it. However, I encourage you to create an Xcode playground and try to come up with the implementation for map, flatten and flatMap for Result or a similar container type (perhaps try with Optional or even Array), and use some sample values to play with them.

And next time you hear the words Functor or Monad, don’t be scared :) They’re simply design patterns to describe common operations that we can perform on different types.