LoginSignup
0
1

More than 3 years have passed since last update.

[Swift] map, flatMapの一般化された説明

Posted at

はじめに

 SwiftにおけるmapflatMapについての記事はQiitaにもたくさんありますが、ArrayOptionalに限定されていて且つそれぞれ別々に説明されている記事が多いような印象を受けました(主観)。
 そこで、(自分が新しく作成した型についてmapで実装すべきかflatMapで実装すべきか迷った時に参照するためにも)この記事ではmapflatMapの特性について簡潔に 一般化された説明をしてみようという次第です。すごく詳細に一般化しようとすると「モナド」というものを理解しないといけないようですが、それは別の記事にお任せしましょう1
 既に同様の記事があったらごめんなさい。あと、この記事ではSwiftに限った話をしていますので悪しからず…。

総論

 mapflatMapも関数(クロージャ)を引数に取るいわゆる高階関数と呼ばれるものですが、元の型・引数となる関数(クロージャ)における返り値の型・高階関数の返り値の型に一定の関係性があり、その関係性によって区別がなされます。「二重が一重になるのがflat」みたいなのは一旦忘れてください。

各論

mapとは

動作

  • Hoge<T>の場合: (T) -> Uという関数(クロージャ)を引数にとって、Hoge<U>を返す。
  • Fuga<T1, T2>の場合: (T1) -> Uという関数(クロージャ)を引数にとって、Fuga<U, T2>を返す

即ち、mapは「引数となる関数(クロージャ)の返り値を同じ型で包み直す」ということです。

  • Array<Element>: func map<T>(_ transform: (Element) throws -> T) rethrows -> Array<T>
  • Optional<Wrapped>: func map<U>(_ transform: (Wrapped) throws -> U) rethrows -> Optional<U>
  • Result<Success, Failure>: func map<NewSuccess>(_ transform: (Success) -> NewSuccess) -> Result<NewSuccess, Failure>

反例…?

  • String: func map<T>(_ transform: (Character) throws -> T) rethrows -> Array<T>
  • Dictionary: func map<T>(_ transform: ((key: Key, value: Value)) throws -> T) rethrows -> Array<T>

➡️これらはSequence由来なので、Sequecne<Element>において(Element) -> Tという関数(クロージャ)を引数にとってSequence<T>を返すと考えれば矛盾はありません。

flatMapとは

動作

  • Hoge<T>の場合: (T) -> Hoge<U>という関数(クロージャ)を引数にとって、Hoge<U>を返す。
  • Fuga<T1, T2>の場合: (T1) -> Fuga<U, T2>という関数(クロージャ)を引数にとって、Fuga<U, T2>を返す

即ち、「引数となる関数(クロージャ)の返り値とflatMap自身の返り値は型が同じ」ということです。

  • Optional<Wrapped>: func flatMap<U>(_ transform: (Wrapped) throws -> Optional<U>) rethrows -> Optional<U>
  • Result<Success, Failure>: func flatMap<NewSuccess>(_ transform: (Success) -> Result<NewSuccess, Failure>) -> Result<NewSuccess, Failure>

あれ?Arrayは…?

Array<Element>flatMapfunc flatMap<SegmentOfResult>(_ transform: (Element) throws -> SegmentOfResult) rethrows -> [SegmentOfResult.Element] where SegmentOfResult: Sequenceとなっています。なので、これはArrayに対するflatMapではなく、前述したようなStringと同様、Sequence<Element>に対するflatMapと捉えなければなりません。つまり、flatMapの引数となる関数(クロージャ)は(Element) -> Sequence<U>と考えることができ、そしてflatMapの返り値もSequence<U>と解釈できます。

注意

 昔のSwiftではflatMapという名前で現在のcompactMapと同じ動作が実装されていました。昔のドキュメントなどを参照して混乱なきよう…。
 ちなみにcompactMapSequence<Element>において.map(_:(Element) -> T?).filter({ $0 != nil })というような動作をします(正しいコードではなくあくまでイメージ)。

エピローグ

 Resultを実装するPR内でAppleの中の人flatMapについてコメントしていた内容にインスパイアされた記事でした。flatMapを「二重になったものを一重にするんだ」と思っていると、ResultにおけるflatMapが「??」となりそうだなと。

0
1
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1