ScalaのOption/Either/Try超入門

この記事は [ウェブクルー Advent Calendar 2019](https://qiita.com/advent-calendar/2019/webcrew) 15日目の記事です。
昨日は @maiunderwood さんの「[みんなに聞いてみた！オススメVS Code拡張機能（2019年版）](https://qiita.com/maiunderwood/private/0213c2b2c7a92f3592df)」でした。

# はじめに
今度引き継いで運用することになったサービスがScalaを使って作られているとのことで、Scalaを学習している最中です。エラー処理を超入門レベルでまとめてみました。

# Option
Scalaは、Javaのクラスを利用でき、その恩恵にあずかることができるのですが、一方でJavaのメソッドを使ったことでnullを受け取ることもありえます。そんな時などに役立つのがOptionです。
Option[A]は、値があるときはSome[A]を返し、値がないときはNoneを返します。

```
scala> Option("あるよ")
res0: Option[String] = Some(あるよ)

scala> Option(null)
res1: Option[Null] = None
````

Optionの値をただ取り出したいのなら、getメソッドを使えば良いのですが、
Noneの場合は例外が帰ってきます。

```
scala> Option("あるよ").get
res2: String = あるよ

scala> Option(null).get
java.util.NoSuchElementException: None.get
  at scala.None$.get(Option.scala:349)
  at scala.None$.get(Option.scala:347)
  ... 29 elided
```
ただ値を取り出したい時にはもっと便利なgetOrElseメソッドがあります。
Noneのときには例外を起こすのではなく、デフォルトの値を得ることができます。

```
scala> Option("あるよ").getOrElse("ないよ")
res4: String = あるよ

scala> Option(null).getOrElse("ないよ")
res5: String = ないよ
```
値が入ってる時のみ実行したい関数がある時にはforeachが使えます。

```
scala> Option("あるよ").foreach(println)
あるよ

scala> Option(null).foreach(println) // 関数は実行されない

```
パターンマッチでもOptionの値を扱うことができます。

```
def printNum(num: Option[Int]): Unit = {
  num match {
  case Some(n) =>
    println(s"$n つあるよ")
  case None =>
    println("ないよ")
  }
}

val fruits = new java.util.HashMap[String, Int]
fruits.put("apple", 1)
val appleNum = Option(fruits.get("apple")) // 1
val orangeNum = Option(fruits.get("orange")) // null
printNum(appleNum) // 「1 つあるよ」が出力される
printNum(orangeNum) // 「ないよ」が出力される
```

# Either
OptionはエラーがあってもNoneの値しかもっていないですが、エラー情報も欲しいというときに役立つのがEitherです。

Eitherは、LeftとRightの2つの値を持ちます。
失敗したときにはLeftに情報を格納し、成功したときにはRightに情報を格納します。

思いつかなかったのであまりよくない例ですが、雰囲気だけでも。。。

```
var total = 99

def checkFruit(fruit: String): Either[String, Int] = {
  if (fruit == "banana") {
    Right(1) // 成功時
  } else {
    Left(s"$fruit はポイント対象外です。") // 失敗時
  }
}

def processPoints(either: Either[String, Int]) : Unit = {
  either match {
    case Right(x) => {total = total + x; println(s"今回で${total}ポイントです")}
    case Left(x)  => println(s"$x 現在は${total}ポイントです")
  }
}

processPoints(checkFruit("banana")) // 「今回で100ポイントです」が出力される
processPoints(checkFruit("apple")) // 「apple はポイント対象外です。 現在は100ポイントです」が出力される
```

foreachを使えば、EitherのRightのときのみ実行したい関数を指定できます。

```
scala> val right:Either[String,Int] = Right(9)     
right: Either[String,Int] = Right(9)

scala> right.foreach(println)
9

scala> val l :Either[String,Int] = Left("lemon")
l: Either[String,Int] = Left(lemon)

scala> l.foreach(println)// Leftのとき関数は実行されない

```

left.foreachを使えば、EitherのLeftのときのみ実行したい関数を指定できます。

```
scala> right.left.foreach(println) //Rightの時関数は実行されない

scala> l.left.foreach(println) 
lemon
```

# Try
Tryは失敗時に例外を保持するFailureと成功時の結果をもつSuccessの値を持ちます。
FailureはThrowableしか入れられず、TryはNonFatalの例外だけをcatchします。

```
scala> import scala.util.Try
import scala.util.Try

scala> val seq: Seq[String] = Seq("apple","orange","lemon")
seq: Seq[String] = List(apple, orange, lemon)

// 例外が起きない場合
scala> val fruit:Try[String] = Try(seq(1))
fruit: scala.util.Try[String] = Success(orange)

// 例外が起きる場合
scala> val fruit2:Try[String] = Try(seq(5))
fruit2: scala.util.Try[String] = Failure(java.lang.IndexOutOfBoundsException: 5)
```

foreachを使えば、Successの時のみ実行したい関数を指定できます。

```
scala> Try(seq(1)).foreach(println)
orange

scala> Try(seq(5)).foreach(println) // 関数は実行されない

```

failed.foreachを使えば、Failureの時のみ実行したい関数を指定できます。

```
scala> Try(seq(1)).failed.foreach(println) // 関数は実行されない

scala> Try(seq(5)).failed.foreach(println)
java.lang.IndexOutOfBoundsException: 5
```

# 参考文献
『Scala実践入門』
https://gihyo.jp/book/2018/978-4-297-10141-1

Standard Library
https://www.scala-lang.org/api/current/scala/index.html

Scala研修テキスト
https://scala-text.github.io/scala_text/error-handling.html

# 最後に
明日は @yagiyuuuuさんです。よろしくお願いいたします！