Verticon
Verticon

Reputation: 2513

Why Does Array<Any>.flatMap() Wrap the Transform Closure Arguments in an Optional?

Below is some playground code that I am using to explore the behavior of Array's implementation of the Sequence protocol's flatMap() method. I was surprised to discover that a consequence of declaring callFlatmap's array parameter as an Array of Any is that flatMap wraps the arguments to the transform closure in an optional. Is this behavior by design?

func echo<T>(_ arg: T) -> T {
    print("arg = \(arg)")
    return arg
}

func callFlatten(_ array: Array<Any>) {
    print("Input: \(array)")
    print("Result: \(array.flatMap{ echo($0) })\n\n")
}

var array = [[1, 2], [3, 4], [5, 6]]

callFlatten(array)
/* Prints:
     Input: [[1, 2], [3, 4], [5, 6]]
     arg = Optional([1, 2])
     arg = Optional([3, 4])
     arg = Optional([5, 6])
     Result: [[1, 2], [3, 4], [5, 6]]
 */

print("Input: \(array)")
print("Result: \(array.flatMap{ echo($0) })\n\n")
/* Prints:
     Input: [[1, 2], [3, 4], [5, 6]]
     arg = [1, 2]
     arg = [3, 4]
     arg = [5, 6]
     Result: [1, 2, 3, 4, 5, 6]
*/

Upvotes: 1

Views: 109

Answers (1)

Martin R
Martin R

Reputation: 539745

Array has two flatMap methods:

/// Returns an array containing the concatenated results of calling the
/// given transformation with each element of this sequence.
public func flatMap<SegmentOfResult : Sequence>(_ transform: (Element) throws -> SegmentOfResult) rethrows -> [SegmentOfResult.Iterator.Element]

and

/// Returns an array containing the non-`nil` results of calling the given
/// transformation with each element of this sequence.
public func flatMap<ElementOfResult>(_ transform: (Element) throws -> ElementOfResult?) rethrows -> [ElementOfResult]

In

var array = [[1, 2], [3, 4], [5, 6]]
// ...
print("Result: \(array.flatMap{ echo($0) })\n\n")
// Result: [1, 2, 3, 4, 5, 6]

the first method is called, because the array element type is known to be an array and thus a Sequence. echo($0) is called with each element, and the result is the concatenation of the inner arrays.

In

func callFlatten(_ array: Array<Any>) {
    print("Input: \(array)")
    print("Result: \(array.flatMap{ echo($0) })\n\n")
}

callFlatten(array)
// Result: [[1, 2], [3, 4], [5, 6]]

nothing is known about the element type, and the second method is called. In order to match the return type ElementOfResult? of the closure, the generic placeholder type of T of echo is inferred as Any? and $0 is promoted to an optional. The result is an array with the non-nil results of echo($0) – which are exactly the array elements again. The type of the result is Array<Any>.

Upvotes: 1

Related Questions