2

Consider this code...

import Foundation

let source = ["A", "B", nil, "D"]
print(type(of:source))

let result1 = source.flatMap{ $0 }
print(type(of:result1))
print(result1)

extension Array
{
    func sameThing() -> Array
    {
        return self.flatMap{ $0 }
    }
}

let result2 = source.sameThing()
print(type(of:result2))
print(result2)

result1 is an Array<String> while result2 is an Array<Optional<String>>. But why?

I've tried using a Sequence instead of an array, but no luck there either.

Mark A. Donohoe
  • 28,442
  • 25
  • 137
  • 286

1 Answers1

1

Your return type preserves generic type

func sameThing(separator:String = " ") -> Array

Therefore on optional elements signature of this operation is equivalent to [T?] -> [T?]

Sequence.flatMap has two overloads with specifically crafted signatures

func flatMap<SegmentOfResult : Sequence>(
    _ transform: (${GElement}) throws -> SegmentOfResult
) rethrows -> [SegmentOfResult.${GElement}]

func flatMap<ElementOfResult>(
    _ transform: (${GElement}) throws -> ElementOfResult?
) rethrows -> [ElementOfResult] {

The second one wins. But since you asked for array of optionals, ElementOfResult becomes Optional<String>, and transform becomes Optional<String> -> Optional<Optional<String>>.

Thus when closure { $0 } returns nil, it gets lifted to Optional(nil), i.e. having .some(nil) value, not .none. Then it will pass the nil guard and appear in result.

paiv
  • 5,491
  • 22
  • 30
  • Interesting! I keep forgetting how Swift uses return types as part of the signature which other languages like C# do not. That said, how would I modify my function to exactly match what flatMap returns? I'm trying to encapsulate some functionality--of which flatMap is part of--so I can reuse it. Kinda stumped there. – Mark A. Donohoe Feb 06 '17 at 04:44