独書会 Scala IN DEPTH @夜のイタリアンカフェ その4

[独書会 Scala IN DEPTH @大人のバーガー屋 その3](http://qiita.com/asmasa/items/82efd4dd473ce14973a2)で記述した、第2章 __The core rules__ の続き。

要約
--

# Nullの代わりにNoneを使う

- `null`を阻止するプログラミングをするには、Scalaはベストな選択である。
- `null`を阻止するために、`scala.Option`を使うことができる。
- `Option`は __あるか・ないか__ のコンテナと考えることができる。
- `Option`には、`Some`と`None`の2つのサブクラスがある。

`null`を許す言語は、`null`は戻り値として致命的でないエラーを示す。
また、 __初期化されていないこと__ を示すプレースホルダとして使われる。

## Scalaでの表現
    
- `Option`のサブクラス`None`で、`null`を表せる。
- `Option`のサブクラス`Some`で、 __初期化された変数__ と __致命的でない変数__ の状態を表せる。

## `Option`の生成

- 値を含まない`Option`は、`None`オブジェクトを通して生成される。
- 値を含む`Option`は、`Some`のファクトリメソッドを通して生成される。

## `Option`の内部の値の取得

### `get`

- `Option`に格納した値にアクセスする
- 存在しない場合は、`Exception`をスローする。
- 他の言語で`null`値を許容するのに似ている。

### `getOrElse`

- 値が存在する場合は、`Option`に格納された値にアクセスしようとする。
- 値が存在しない場合は、メソッドに指定した値を返す。

__`get`よりも`getOrElse`を常に選ぶべきである。__

## `Option`のファクトリメソッド

`Option`のコンパニオンオブジェクトでは、ファクトリメソッドを提供している。

- 入力が`null`なら`None`を作る。
- 入力が初期化されているなら`Some`を作る。

`Option`にラップすることは、信頼出来ないソース(別のJVM言語とか)からの入力を受け取ることを簡単にする。

コードで`null`チェックする箇所がシンプルになる。
`Option`は、`null`を使ったり`if`チェックするよりも高度な機能を提供する。

## `Option`のテクニック

- `Option`の最大の特徴は、 __collection__ のように扱えること。
- `map`, `flatMap`, `foreach`メソッドを使うことができるし、内部で式を利用することもできる。
- これにより簡潔なシンタックスで書けるし、初期化されていない値を処理する様々なメソッドを使える。

### オブジェクトを生成するかデフォルトを返すか

> ```scala:Listing&nbsp;2.17&nbsp;Creating&nbsp;an&nbsp;object&nbsp;or&nbsp;returning&nbsp;a&nbsp;default
def getTemporaryDirectory(tmpArg: Option[String]): java.io.File = {
￼￼
tmpArg.map(name => new java.io.File(name)).
    filter(_.isDirectory).
    getOrElse(new java.io.File(
      System.getProperty("java.io.tmpdir")))
￼￼￼￼￼￼}
```
> (Joshua D. Suereth, Scala in Depth, p.35)

ここでは、以下の場合に適切なテンポラリディレクトリを返したい。

- ユーザが新しいファイルを指定しない場合
- ユーザが与えた引数が存在しないディレクトリの場合
- ディレクトリを提供できなかった場合

`if`文やブロックのネストに頼ることなく強力なチェックを可能にする。
特定のパラメータの利用に基づいたコードブロックを実行したいときには、ブロックを好んで使う。

### 変数が初期化されていれば、ブロックを実行する

> ```scala:Listing&nbsp;2.18&nbsp;Executing&nbsp;code&nbsp;if&nbsp;option&nbsp;is&nbsp;defined
val username: Option[String] = ...
for(uname <- username) {
   println("User: " + uname)
}
```
> (Joshua D. Suereth, Scala in Depth, p.36)

- `Option`は値を含む場合に、コードブロックを実行することができる。
- `foreach`メソッドで実現できる。つまり、`Option`内の全ての要素を反復処理する。
- `Option`は、0 or 1つの値だけを含む。これは、ブロックが __実行される__ か __無視される__ かを意味する。
- このシンタックスは特に式に適している。

> ```scala:Listing&nbsp;2.19&nbsp;Executing&nbsp;code&nbsp;if&nbsp;several&nbsp;options&nbsp;are&nbsp;defined
def authenticateSession(session: HttpSession,
                        username: Option[String],
                        password: Option[Array[Char]]) = {
   for(u <- username;
       p <- password;
       if canAuthenticate(u, p)) {
     val privileges = privilegesFor(u)
     injectPrivilegesIntoSession(session, privileges)
￼￼} }
```
> (Joshua D. Suereth, Scala in Depth, p.36)

ユーザ認証をして、セキュリティトークンをHTTPSessionにインジェクションする例。 
こうしておけば、後でフィルタとサーブレットがアクセス権限をチェックできる。

- この例では、式で条件ロジックを組み込んでいる。少ないネストで論理ブロックを保持するのに役に立つ。
- 全てのヘルパーメソッドが`Option`クラスを使う必要ないことは、重要なことである。
- `Option`は、初期化していない変数の最前線の防御として機能する。`Option`がコードの残りを汚染する必要はない。
- 引数としての`Option`は、 __初期化されていない・初期化されている可能性__ があることを意味する。
- メソッドが`Option`として受け取らないなら、値を`null` or __初期化されていないパラメータ__ で渡すべきではない。


### 別の変数を生成するために、潜在的に初期化されていない変数を使う

> ```scala:Listing&nbsp;2.20&nbsp;Merging&nbsp;options
def createConnection(conn_url: Option[String],
                    conn_user: Option[String],
                    conn_pw: Option[String]) : Option[Connection] =
  for {
    url <- conn_url
    user <- conn_user
    pw <- conn_pw
  } yield DriverManager.getConnection(url, user, pw)
```
> (Joshua D. Suereth, Scala in Depth, p.37)


初期化されていない可能性がある変数の集合を変換したい場合がある。
この時は`for`式を使い、同時に`yield`も使う。

#### `lift`関数

> ```scala:Listing&nbsp;2.21&nbsp;Generically&nbsp;converting&nbsp;functions
scala>  def lift3[A,B,C,D](
     |  f: Function3[A,B,C,D]): Function3[Option[A], Option[B],
     |                                    Option[C], Option[D]] = {
     |     (oa : Option[A], ob : Option[B], oc : Option[C]) =>
     |        for(a <- oa; b <- ob; c <- oc) yield f(a,b,c)
     |   }
lift3: [A,B,C,D](f: (A, B, C) => D)(Option[A],
                                       Option[B],
                                       Option[C]) => Option[D]
scala> lift3(DriverManager.getConnection)
res4: (Option[java.lang.String],
       Option[java.lang.String],
       Option[java.lang.String]) => Option[java.sql.Connection] =
  <function3>
```
> (Joshua D. Suereth, Scala in Depth, p.37)

- `lift3`メソッドは、唯一のパラメータとして関数を受け取ることを除いて、先ほどの`createConnection`メソッドに似ている。
- `Function3`traitは、3つの引数を受け取り結果を返す関数である。
- `lift3`関数は、入力として3つの引数の関数を受け取り、3つの引数の関数を出力する。
- REPLの出力からわかるように、`Option`フレンドリーな関数を作るために、既存の関数に対して使うことができる。

直接`DriverManager.getConnection`メソッドを受け取り、初期の`createConnection`メソッドと意味的に等価な関数になる。

__初期化されていない変数のカプセル化で使われる時、このテクニックはうまく動作する。__

初期化されていると仮定して、ユーティリティメソッドも含めてコードのほとんどで書くことができる。
そして必要な時に、これらの関数を`Option`フレンドリーの変種にする。

重要な点は、`Option`に含まれるものからequalityとhashCodeを導出することである。
__Scalaでは、`equality`と`hashCode`理解することは、特に多態では重要である。__

# Rule
## 4. `null`の代わりに`None`を使う
- Javaでは値を`null`で初期化するのが習慣であったが、Scalaでは同じ目的で`Option`が用意されている。
- `None`を使うと、意図しない __null pointer__ を防げる。

所感
--

- `Option`はどこで使うかを見極めるのが重要。なんでもかんでも使うと、逆に簡潔性が失われる。
- 複数の`Option`型の変数を扱うのに __for-comprehension__ を使うのは有用。
- 同一ブロック内で、ある`Option`型の変数を2回以上評価させたら負けかな。
    - つまり、関数の戻り値を`Option`型で変数に入れるのは、いけていない。値を __取り出す前__ と __取り出した後__ の変数名で悩むため。。。