Swift: more confusion about flatMap

With Swift 2.0 the flatMap function on SequenceType has been introduced. It seems to me that a lot of developers are confused about this function and what it does and what are the differences between it and the map function. In this post i will try to confuse you a bit more about the flatMap function.

Back to the roots

In the pure functional programming there is a clear definition of flatMap
flatMap:([A],(A -> [B])) -> [B] . It takes a list of elements of type A and a function A -> [B] that produces a list of B for each element of the original list. In the first step flatMap applies the function f on each element of the list (exactly as map) producing a list of lists of type B. In the second step it combines the inner lists producing a flattened list of type B. For example (pseudocode)

let list:[Int] = [1, 2, 3]
let f(x: Int) -> [Int] = [x, x+1]

list.flatMap(f) //would produce this list [1, 2, 2, 3, 3, 4]

The Swift implementation

I think the root of the confusion comes because Swift introduces two overloaded implementations of the flatMap function in SequenceType .

The first one looks like (a little bit simplified):

extension SequenceType {
  public func flatMap(transform: (Self.Generator.Element) -> T?) -> [T] {
    var result: [T] = []
      for element in self {
        if let newElement = transform(element) {
          result.append(newElement)
        }
      }
      return result
   }
}

This implementation is very similar to the map function with some small differences. The function used for the transformation returns an optional of T and because of the statement

if let newElement = transform(element) 

the transformed elements are unwrapped and just appended to the result list if they are not nil.

The second implementation is defined as following (a little bit simplified):

extension SequenceType {
  func flatMap<S : SequenceType>(transform: (Self.Generator.Element) -> S) -> [S.Generator.Element] {
    var result: [S.Generator.Element] = []
    for element in self {
      result.appendContentsOf(transform(element))
    }
    return result
  }
}

This is the Swift implementation of flatMap that fits to the definition in the functional programming i mentioned earlier. In this case, the transformation function returns a sequence. flatMap the combines the resulting sequences to one sequence using appendContentsOf

Consider the following Swift example:

let list:[Int?] = [1, nil, 3]
let result = list.flatMap{$0}

print(result) // would prints [1, 3]

Because the transformation function, in this case $0 returns one element and not a sequence, the compiler picks the first implementation of flatMap and cuts the nils from the result.

Now consider an another Swift example:

let list:[Int] = [1, 2, 3]
let result = list.flatMap{ x in [x, x+1]}

print(result) // would prints [1, 2, 2, 3, 3, 4]

Because the transformation function, in this case x in [x, x+1] returns a sequence, the compiler picks the second implementation of flatMap and returns a transformed and flattened sequence.

Still not confused enough?

Then have a look at the flatMap implementation in the ReactiveX programing for example in RxSwift. The definition there is:

transform the items emitted by an Observable into Observables, then flatten the emissions from those into a single Observable

Leave a Reply

Your email address will not be published. Required fields are marked *