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