Optional map chaining behavior

Hi, I'm trying to make some optional unwrapping using Optional.map but I'm finding some strange behavior when I try to map nested Optional values. Here is an example:

struct Foo {
    let attr: Int?
}

let f: Foo?

f.map { $0.attr }
 .map { attr in
       // attr here is Int? should it be Int because previous map?
  }

The behavior is intended? If so, why it only works with the first level optional?

The map you are looking for is compactMap

EDIT:
ah, there is no compactMap for Optional, my bad
EDIT 2:
there is still flatMap avialiable

Using optional unwrapping also does the trick
f .map { $0.attr }? .map { attr in attr is Int // prints true }

I tried using Optional.flatMap instead and it worked

f.flatMap { $0.attr }
 .map { attr in
       // attr is Int
  }

Optional<A>.map takes a function (A) -> B and returns an Optional<B>.
Optional<A>.flatMap takes a function (A) -> Optional<B> and returns an Optional<B>.

So, if we just follow the types, we see that since the closure { $0.attr } is a function (Foo) -> Optional<Int>, passing it to Optional<Foo>.map would return a value of type Optional<Optional<Int>>, while passing it to Optional<Foo>.flatMap would return a value of type Optional<Int>.

This is why, in this case, flatMap works for you.

In fact, the flatMap + flatMap + ... + map pattern is quite common in functional programming precisely because it allows you to chain sequences of operations that all return wrapped structures (in this case, wrapped in optionals), while only having a single wrapper at the end. Some languages have extra syntactic sugar for that.

To some extent, Swift also has some syntactic sugar around that with optional chaining (although that is limited to variable/method lookup), so in this case you might even simply write f?.attr.map { ... }, or even f?.attr?.somethingDefinedOnInt (like f?.attr?.magnitude).

3 Likes

Thanks for your explanation. Now it makes a lot of sense. :grin:

Terms of Service

Privacy Policy

Cookie Policy