yukia blog

すごいブログ

SwiftのOptionalと恋人以上の関係になりたい(その1)

はじめに

SwiftOptionalについて、こんな認識がありました。

Int?Int が入るかもしれないしnilが入るかもしれない型

これだけの認識でもSwiftのプログラミングは十分可能だと思います。

Optional BindingOptional Chainingを使いこなせれば、Optionalと友達以上恋人未満の関係になったと言っていいでしょう。

ただ、それだけでは満足しきれない、もっとOptionalのことを知りたい!恋人以上の関係になりたい!と思うようになってきました。

Optionalの定義

Optionalの定義を確認してみます。(いろいろ省いています。

public enum Optional<Wrapped> {
    case None
    case Some(Wrapped)
}

この時点でOptionalの認識が少し改まります。

Int?Some(Int) が入るかもしれないしNoneが入るかもしれないOptional<Int>という名の箱のようなもの

おお、nilとかどこにも出てきませんね。。。

普段何気なく書いているコードを、enumを使ったコードっぽく書きなおしてみます。

let name: String? = "yukia"
let age: Int? = nillet name: Optional<String> = .Some("yukia")
let age: Optional<Int> = .None

こういう書き方もできるわけです。

let name: Optional<String> = String("yukia")
let age: Optional<Int> = nil

これって普通じゃん、と思うかもしれませんが何気にすごい事です。

何がすごいかって、Optional<String>Stringを代入しちゃってるってことです。Swiftは静的型付け言語なのででは暗黙的型変換は許されないし、Array<String>Stringを代入するなんてことも普段考えませんよね?でもそれと同じことです。

なぜこのようなことが許されるのか、この一言に尽きると思います。

Optionalは例外として暗黙的型変換が許されている

おお、OptionalはSwiftでは特別扱いされているんですね!!!すごい!!!

Optional Binding

Optionalという箱から値を取り出して利用する方法の一つ、Optional Binding。

こちらも普段何気なく書いているコードと、enumを使ったコードとで比較してみましょう。

一般的な記述
let name: String? = "yukia"

if let unwrapedName = name {
    print(unwrapedName)
} else {
    print("unknown")
}

if let ... と書くことで、中の値を取り出していますね。取り出せない場合にはelseに入ります。 これをenum的に記述すると以下のようになるでしょう。

let name: String? = "yukia"

switch name {
case .Some(let unwrapedName):
    print(unwrapedName)
case .None:
    print("unknown")
}

もちろんパターンマッチさせることも可能です。

Optional Chaining

それなりに使うことができていると自負していますが、もう少し掘り下げてみたいOptional Chaining。

まずはOptinal Chainingの基本的な動作を確認してみます。名前を大文字にしてかっこいい感じにしましょう。

Some(値あり)
let name: String? = "yukia"
print(name?.uppercaseString)  // 出力:Optional("YUKIA")
None(値なし)
let name: String? = nil
print(name?.uppercaseString)  // 出力:nil

nilならばnilを返すし、それ以外ならメソッドを適用して返しています。そういえばOptionalにはこんなメソッドが宣言されていました。

/// If `self == nil`, returns `nil`.  Otherwise, returns `f(self!)`.
@warn_unused_result
@rethrows public func map<U>(@noescape f: (Wrapped) throws -> U) rethrows -> U?
/// Returns `nil` if `self` is nil, `f(self!)` otherwise.
@warn_unused_result
@rethrows public func flatMap<U>(@noescape f: (Wrapped) throws -> U?) rethrows -> U?

map flatMapどちらでも似たようなことができるのですが、ここではflatMapで説明します。

flatMapは、nilならばnilを返すし、それ以外ならメソッドを適用して返すメソッド

Optional Chaining挙動と似ていますね。そう、つまりは、Optional ChainingはflatMapを用いて記述できるということです。

let name: String? = "yukia"
let upperName = name.flatMap { $0.uppercaseString }
print(upperName)

もっと長い複雑なOptional ChainingもflatMapを使ってメソッドチェーンで記述することが可能です。

Optional ChainigはflatMapの構文糖衣である

といえるでしょう。

その1終わり

さて、この辺までくればOptionalのこともだいぶわかってきたと思います。恋人以上とは言えないが、好感度も上がり親密な関係になってきたことでしょう。

なんとなく漂ってくる雰囲気がHaskellMaybeに似ている、、と思いました。ただここは浮気せずにもうちょっとOptionalについて学んで行こうと思います。

次回はもっと踏み込んだところまで解説できればいいなと思います。