SwiftのOptional
にはmap
がある.
func map<U>(f: T -> U) -> U?
引数のf
はT
(Optional
で包まれいる値)を受け取ってU
を返す関数を受け取って,f
の返り値であるU
をOptional
で包んだU?
を返す関数です.
要は,if-let
を使って明示的にunwrapしなくてもOptional
の構造を保ったまま中の値に関数を適用するための関数ということですね.
このmap
ですが,Optional
な値を返す関数と使ってみると案外使いにくいことがわかります.
let URL: NSURL? = NSURL(string: "http://yashigani.hatenablog.com") if let URLString = URL.map({ $0.absoluteString }) { // URLStringはString型を期待 println(URLString) // => Optional("http://yashigani.hatenablog.com") }
Optional
を返す関数を適用する場合,map
の返り値は二重にOptional
に包まれて返ってきてしまうのでif-let
でunwrapしてもOptional
が残ってしまいます.
つまり,実装は以下のようになっていることが予想できます.
func map<U>(f: T -> U) -> U? { switch self { case .None: return nil case .Some(let x): return .Some(f(x)) } }
このままでは使いにくいです.
ところで,普通の値をとって何かに包まれた値を返す関数を,同じ何かに包まれた値に適用するといえばなにか思い出さないでしょうか?
そう,モナドですね.
Optional
のmap
はモナドにおけるバインドに似た動作をします.
ということはモナドっぽく拡張すればOptional
を返す関数に対しても使いやすくできるのではないでしょうか.
以下のように拡張してみます.
extension Optional { func bind<U>(f: T -> U?) -> U? { switch self { case .None: return nil case .Some(let x): return f(x) } } }
実行結果は以下のようになります.
if let URLString = URL.bind({ $0.absoluteString }) { println(URLString) // => http://yashigani.hatenablog.com } if let URLString = URL.bind({ (x: NSURL?) -> String? in return nil }) { println(URLString) // URLStringはnilになるので実行されない }
狙いどおりいいかんじの動作になりました. 同僚のid:cockscombがはまってて,こうやったらいいんじゃない?ってアドバイスしたらおもったより学びがあった. はてなでの生活は毎日が学びです. 圧倒的感謝です.
余談
Optional
の実装を見たらmap
のコメント間違ってた.
/// If `self == nil`, returns `nil`. Otherwise, returns `f(self!)`. func map<U>(f: (T) -> U) -> U?
返ってくるのはf(self!)
じゃなくてf(self!)?
です.
今日のお得情報
Swiftは関数型パラダイムの影響を色濃く受けているプログラミング言語なので,少しだけでも関数型プログラミングのエッセンスを学んでおくととても役に立ちます. 簡単に学ぶには以下の書籍がオススメです.
- 作者: Miran Lipovaca
- 出版社/メーカー: オーム社
- 発売日: 2012/09/21
- メディア: Kindle版
- 購入: 4人 クリック: 9回
- この商品を含むブログを見る
読んだ人の感想もご紹介します.
Haskell勉強しててよかったな〜ってSwift書くようになってから毎日おもってる
— チキンとタイカレー (@yashigani) 2015, 1月 8