OptionalのmapとflatMapを読み解く
Swiftがオープンソース化されたことにより、私たちでも標準ライブラリの実装を読むことができるようになりました。そこで、今回はOptional
型のmap()
とflatMap()
の2つを読んでみて、どういう違いがあるのかを実装面から考えてみようと思います。
Optional
型の定義は、stdlib/public/core/Optional.swift ファイルにあります。
public enum Optional<Wrapped> : _Reflectable, NilLiteralConvertible { case None case Some(Wrapped) :
今更かもしれませんが、Optional型とは様々な型のオブジェクトをSomeのAssociated Valueとして持てるenumのことです。ちなみにWrapped
は単なるジェネリック型T
のtypealiasで、上のcase文のすぐ後で、次のように定義されている型です。
@available(*, unavailable, renamed="Wrapped") public typealias T = Wrapped
さて、そんなOptional
型のmap()
とflatMap()
の2つがどのように実装されているかというと、下に示すようにとてもシンプルな実装になっていました。
public func map<U>(@noescape f: (Wrapped) throws -> U) rethrows -> U? { switch self { case .Some(let y): return .Some(try f(y)) case .None: return .None } } public func flatMap<U>(@noescape f: (Wrapped) throws -> U?) rethrows -> U? { switch self { case .Some(let y): return try f(y) case .None: return .None } }
どちらも.Some
か.None
かで処理を分岐させて、.Someのときのみ引数の関数をアンラップした値と共に呼んでいます。ですが、map()
は.Someだった場合に実施する結果を再び.SomeでOptionalにしてから返していること、flatMap()
は結果をアンラップしたまま返していることが違っています。その結果としてmap()
はOptional型のままの値が得られ、一方でflatMap()
はアンラップした値に変わります。
というわけで、実装から見たOptional型のmapとflatMapの違いは、結果をアンラップするかどうかだけ、と解釈できそうです。