戦略 Scala 日記

素人プログラマの思考のセンス

Scalaでモナドを、なんとなくイメージするためのヒント

Scalaモナドを説明する際には、まずリストを用いるのがイメージしやすい。

scala> List(2, 3, 5, 7, 11, 13, 17, 19).foreach(println)
2
3
5
...

このListの値に効果を与えて、与えられたものを関数(ここではprintln)でシーケンシャルに評価している。

次にScalaの代表的な型としてOptionがあるが、これもモナドである。 Optionは値をもつ、持たないそれぞれを、SomeNoneという箱で表す。

scala> Option(10)
res: Option[Int] = Some(10)

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

これに対して、リストと同じようにmapや、filterfoearchなどのメソッドを使うことができる。

scala> Option(10).foreach(println)
10

scala> Option(10).map(_ * 2).foreach(println)
20

また、例外処理が発生する場合に、このOptionをつくるための方法として、scala.util.Tryがある。 これによって、生成されるOptionに対して、リストと同様な処理をすることができる。

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

// 例外が発生しないパターン

scala> Try( "10".toInt ).toOption
res: Option[Int] = Some(10)

scala> Try( "10".toInt ).toOption.foreach(println)
10

// 例外が発生するパターン

scala> Try( "fuga".toInt ).toOption
res: Option[Int] = None

scala> Try( "fuga".toInt ).toOption.foreach(println)

また、Optionと同様に、Futureもモナドとして扱うことができる。 Furute[Any]に対して、やはりリストと同様に、mapforeachなどを適用できる。 Futureの場合は、処理が行われた後に与えた関数を評価していく。

scala> import scala.concurrent._

scala> import ExecutionContext.Implicits.global

scala> val f: Future[Int] = future { (1 to 100).foldLeft(0)((x: Int, y: Int) => x + y) }
warning: there was one deprecation warning; re-run with -deprecation for details
f: scala.concurrent.Future[Int] = scala.concurrent.impl.Promise$DefaultPromise@ca213f

scala> f.foreach(println)
5050