flatMap on optional - Purpose?

I just stumbled upon a usage of flatMap on optionals.

Code

var a : Int? = nil

let x = a.flatMap { _ in return 10 }

print("x = \(String(describing: x))")

Output:

x = nil

if a is nil, then flatMap returns nil else it returns 10

Question / Doubt:

I was just wondering if there a reason why flatMap exists for optionals because map does the same thing on optionals.

It does not do the same thing, but due to Swift automatically promoting values as needed to Optionals, you're seeing the same result.
The difference between map and flatMap is that the given transform in one case returns T, in the other T?.

A better snippet for your example would be

var a : Int? = nil

let x = a.flatMap { $0 > 0 ? $0 : nil }

print("x = \(String(describing: x))")
1 Like

Thanks,

In your example if a = 5 map returns Optional(Optional(5)) while flatMap returns Optional(5)

That's right. Because map's generic parameter in inferred as Int?, hence it returns Int??, while flatMap's generic parameter is inferred as Int , hence it returns Int?.

func map<U>(_ transform: (Wrapped) throws -> U) rethrows -> U?
var a : Int? = nil
let x = a.map { $0 > 0 ? $0 : nil } // U == Int? -> U? == Int??

func flatMap<U>(_ transform: (Wrapped) throws -> U?) rethrows -> U?
var a : Int? = nil
let x = a.flatMap { $0 > 0 ? $0 : nil } // U == Int -> U? == Int?

In general, map() transforms each element to another type, leaving the outer wrapper type alone. On the other hand, flatMap() transforms each element to *the wrapper type* (with another element type), and then β€œflattens” all the results into a single wrapper.

Here is a summary of how they work for Array, Optional, and a generic wrapper type W:

.map() Input Transform Output
Array [T] T β†’ U [U]
Optional T? T β†’ U U?
Generic W<T> T β†’ U W<U>


.flatMap() Input Transform Output
Array [T] T β†’ [U] [U]
Optional T? T β†’ U? U?
Generic W<T> T β†’ W<U> W<U>
4 Likes

You can also say, in a more general way, that map maps A -> B to F(A) -> F(B),
while flatMap maps A -> F(B) to F(A) -> F(B) :)

(In flatMap, flat is likely an allusion to the return type being left untransformed)

Thanks Nevin and Anthony,

Nevin: Is map and flatMap interchanged in the table you provided ?

For optionals in flatMap the transformation mentioned is T β†’ U?, but that seems more like map.

Am I missing something ?

I know this question is to Nevin, but I guess it won't hurt if I answer since I'm here.

This is the flatMap signature for Optional:

func flatMap<U>(_ transform: (Wrapped) throws -> U?) rethrows -> U?

It maps, like any other flatMap, A -> F(B) to F(A) -> F(B)

In the flatMap for Optional<Wrapped>, A == Wrapped, F(B) == U?, F(A) == Wrapped?.

So A -> F(B) to F(A) -> F(B) becomes Wrapped -> U? to Wrapped? -> U?

For map, the "transform" is always A -> B. For flatMap, it's always A -> F(B), as you see in Nevin's table.

Thanks Antony and others, the examples helped. I will try out more examples.