Maxim Veksler
Maxim Veksler

Reputation: 30182

flatMap does not filter out nil when ElementOfResult is inferred to be Optional

Swift documentation of flatMap reads:

Returns an array containing the non-nil results of calling the given transformation with each element of this sequence.

In the following examples when return type of ElementOfResult is left to the compiler to infer flatMap works as documented, yet on line 5 when ElementOfResult is specified, thus inferred to an Optional<String> type it seems that flatMap stops filtering out nil's.

Why is it doing that?

~ swift
Welcome to Apple Swift version 3.0.2 (swiftlang-800.0.63 clang-800.0.42.1). Type :help for assistance.
  1> let words = ["1989", nil, "Fearless", nil, "Red"]
words: [String?] = 5 values {
  [0] = "1989"
  [1] = nil
  [2] = "Fearless"
  [3] = nil
  [4] = "Red"
}
  2> words.flatMap { $0 }
$R0: [String] = 3 values {
  [0] = "1989"
  [1] = "Fearless"
  [2] = "Red"
}
  3> let resultTypeInferred = words.flatMap { $0 }
resultTypeInferred: [String] = 3 values {
  [0] = "1989"
  [1] = "Fearless"
  [2] = "Red"
}
  4> let resultTypeSpecified: [String?] = words.flatMap { $0 }
resultTypeSpecified: [String?] = 5 values {
  [0] = "1989"
  [1] = nil
  [2] = "Fearless"
  [3] = nil
  [4] = "Red"
}

Upvotes: 1

Views: 790

Answers (1)

Kevin
Kevin

Reputation: 17556

Here's the definition of flatMap()

public func flatMap<ElementOfResult>(_ transform: (Element) throws -> ElementOfResult?) rethrows -> [ElementOfResult]

When you set the type of resultTypeSpecified to [String?], you tell the compiler that ElementOfResult is Optional<String>.

Your transform closure has a type of (String?) -> Optional<Optional<String>>.

flatMap will take away 1 "layer" of optionals but not 2 layers.

Hopefully this example will makes things clearer:

let input: [String??] = [
    Optional.some(Optional.some("1989")),
    Optional.some(Optional.none),
    Optional.some(Optional.some("Fearless")),
    Optional.some(Optional.none),
    Optional.some(Optional.some("Red"))
]

let output = input.flatMap({ $0 })

Upvotes: 2

Related Questions