By Shashank Mishra


2020-06-28 15:08:11 8 Comments

Consider the following array.

let marks = ["86", "45", "thiry six", "76"]

I've explained my doubts in the following two cases.

Case#1

// Compact map without optionals
let compactMapped: [Int] = marks.compactMap { Int($0) }
print("\(compactMapped)")

Result - [86, 45, 76]

Case#2

// Compact map with optionals
let compactMappedOptional: [Int?] = marks.compactMap { Int($0) }
print("\(compactMappedOptional)")

Result - [Optional(86), Optional(45), nil, Optional(76)]

Why there is "nil" in the result of Case#2? Can anyone explain why is it not like this [Optional(86), Optional(45), Optional(76)] in Case#2? (PFA playground)

enter image description here

1 comments

@matt 2020-06-29 16:41:47

I submitted this behavior as a bug at bugs.swift.org, and it came back as "works as intended." I had to give the response some thought in order to find a way to explain it to you; I think this re-expresses it pretty accurately and clearly. Here we go!

To see what's going on here, let's write something like compactMap ourselves. Pretend that compactMap does three things:

  1. Maps the original array through the given transform, which is expected to produce Optionals; in this particular example, it produces Int? elements.

  2. Filters out nils.

  3. Force unwraps the Optionals (safe because there are now no nils).

So here's the "normal" behavior, decomposed into this way of understanding it:

let marks = ["86", "45", "thiry six", "76"]
let result = marks.map { element -> Int? in
    return Int(element)
}.filter { element in
    return element != nil
}.map { element in
    return element!
}

Okay, but in your example, the cast to [Int?] tells compactMap to output Int?, which means that its first map must produce Int??.

let result3 = marks.map { element -> Int?? in
    return Int(element) // wrapped in extra Optional!
}.filter { element in
    return element != nil
}.map { element in
    return element!
}

So the first map produces double-wrapped Optionals, namely Optional(Optional(86)), Optional(Optional(45)), Optional(nil), Optional(Optional(76)).

None of those is nil, so they all pass thru the filter, and then they are all unwrapped once to give the result you're printing out.

The Swift expert who responded to my report admitted that there is something counterintuitive about this, but it's the price we pay for the automatic behavior where assigning into an Optional performs automatic wrapping. In other words, you can say

let i : Int? = 1

because 1 is wrapped in an Optional for you on the way into the assignment. Your [Int?] cast asks for the very same sort of behavior.

The workaround is to specify the transform's output type yourself, explicitly:

let result3 = marks.compactMap {element -> Int? in Int(element) }

That prevents the compiler from drawing its own conclusions about what the output type of the map function should be. Problem solved.

[You might also want to look at the WWDC 2020 video on type inference in Swift.]

@matt 2020-06-30 02:44:35

Okay, this was a lot more interesting than I was expecting! Provided both an explanation and a workaround.

Related Questions

Sponsored Content

9 Answered Questions

[SOLVED] Swift Beta performance: sorting arrays

2 Answered Questions

[SOLVED] Difference between flatMap and compactMap in Swift

4 Answered Questions

[SOLVED] Unwrapping an optional in Swift 4

4 Answered Questions

[SOLVED] What is an "unwrapped value" in Swift?

  • 2014-06-04 10:02:40
  • Bigood
  • 76812 View
  • 155 Score
  • 4 Answer
  • Tags:   ios swift ios8

5 Answered Questions

[SOLVED] Why aren't [String:AnyObject?] and [String:AnyObject] the same type to the swift compiler?

  • 2016-03-25 13:34:28
  • Quantaliinuxite
  • 78 View
  • 0 Score
  • 5 Answer
  • Tags:   swift

1 Answered Questions

[SOLVED] Unwrapping optional inside of closure using reduce

  • 2015-08-29 00:42:17
  • Guillermo Alvarez
  • 367 View
  • 0 Score
  • 1 Answer
  • Tags:   swift

1 Answered Questions

[SOLVED] Using let and if together - Swift Guided Tour Playground

2 Answered Questions

[SOLVED] Conversion of optional valued expressions to boolean

  • 2014-06-13 11:48:11
  • Steve Waldman
  • 3729 View
  • 2 Score
  • 2 Answer
  • Tags:   swift

Sponsored Content