[Swift4.1] compactMap(_:)メソッドの導入経緯のご紹介

こちらの[プロポーサル](https://github.com/apple/swift-evolution/blob/master/proposals/0187-introduce-filtermap.md)を検証してまとめました。

# 非推奨になったメソッド

Swift4.1から以下のflatMap(_:)メソッドが非推奨となりました。

```swift:Sequence
func flatMap<U>(_:(Element) -> U?) -> [U]
```

あわせてこちらも非推奨になった。

```swift:Collection
func flatMap(_: (Element) -> String?) -> [String]
```

# 追加されたメソッド

代わりに以下のメソッドが追加されました。

```swift:Sequence
func compactMap<U>(_ transform: (Element) throws -> U?) rethrows -> [U]
```

---

よく見ると、どうやらメソッド名がリネームされただけのようです。
どうしてわざわざリネームしたのでしょうか？

# 動機

上記の非推奨にされたflatMap(:_)メソッドは、以下のように配列の値を変更しつつ、フィルタリングも行いたい時に使用されていた方が多いかと思います。
(mapはフィルタリングできないし、filterは型の変換が行えない。ところがこいつは両方行える。)

```swift:文字列から数字のみ取り出す
 10> "H1e2l3l4o5Wo7l8d".flatMap { Int.init(String($0)) }
$R9: [Int] = 7 values {
  [0] = 1
  [1] = 2
  [2] = 3
  [3] = 4
  [4] = 5
  [5] = 7
  [6] = 8
}
```

上記、 `Int.init` の結果が失敗した場合、nilが返され、その値は出力される配列から除外されます。

しかしながら、本来のflatMap(_:)メソッドは、Haskellのリストモナドが由来となっており、今回その由来元のメソッドは無事に残されております。

今回残ったflatMap(_:)メソッド

```swift:Sequence
Sequence.flatMap<S>(_: (Element) -> S) -> [S.Element] where S: Sequence
```

上記のメソッドの役割は、リストモナドで検索をかけていただけたらと思います。

ではなぜその他の２つのflatMap(_:)メソッドを非推奨にして、代わりにあらたにリネームしただけの関数を追加したかと言うと以下の問題がswift4で発生するようになったためです。

# swift4から **String** が **Sequence** となった。

従って、以下names1とnames2は、同様の結果を返すように見えますが、異なる結果が返る用になりました。

```swift:配列から名前を取り出す
struct Person {
	var name: String
	init(_ name: String) { self.name = name }
}

func getNames(_ people: [Person]) -> [String] {
	return people.flatMap { $0.name }
}

let people = [Person("tanaka"), Person("yoshida")]
let names1 = getNames(people)
print("names1: \(names1)")
let names2 = people.flatMap { $0.name }
print("names2: \(names2)")
```

```console:swift3.1での実行結果
names1: ["tanaka", "yoshida"]
names2: ["tanaka", "yoshida"]
```

```console:swift4.0での実行結果
names1: ["tanaka", "yoshida"]
names2: ["t", "a", "n", "a", "k", "a", "y",”o” "s", "h", "i", "d", "a"]
```

Swift4以前は、StringはSequeceではなかったため、
どちらもCollection側の *flatMap(_:)* メソッドが使用されたのに対し、Swift4.0の方では、 names1 が非推奨にされたCollection側の *flatMap(_:)* メソッドが使われ、 names2 は、 今回残った本来の *flatMap(_:)* メソッドが使用されたためです。

# 結論

とういこともあり、今回非推奨にした *flatMap(_:)* メソッドは本来のリストモナドのための *flatMap(_:)* メソッドと名前がかぶっておかしいし、Swift4からStringがSequenceになって更に上記のメソッドとぶつかってしまい、紛らわしいしということもあり、 **compactMap(_:)** へとリネームされてしまいました。

# 参照
[swift-evolution/0187-introduce-filtermap.md at master · apple/swift-evolution](https://github.com/apple/swift-evolution/blob/master/proposals/0187-introduce-filtermap.md)
> Introduce Sequence.compactMap(_:)


[flatMap(_:) - Sequence | Apple Developer Documentation](https://developer.apple.com/documentation/swift/sequence/2905332-flatmap)
> func flatMap<SegmentOfResult>(_ transform: (Self.Element) throws -> SegmentOfResult) rethrows -> [SegmentOfResult.Element] where SegmentOfResult : Sequence


[flatMap(_:) - Sequence | Apple Developer Documentation](https://developer.apple.com/documentation/swift/sequence/2907182-flatmap)
> func flatMap<ElementOfResult>(_ transform: (Self.Element) throws -> ElementOfResult?) rethrows -> [ElementOfResult]


[flatMap(_:) - Array | Apple Developer Documentation](https://developer.apple.com/documentation/swift/array/2903427-flatmap#declarations)
> func flatMap(_ transform: (Element) throws -> String?) rethrows -> [String]