LoginSignup
6

More than 5 years have passed since last update.

scalaz - Future

Last updated at Posted at 2015-02-07

Future

scalaz-concurrentのFutureを使ってみる。バージョンは7.1だ。

scala.concurrent.Futureと同じ意味だが、それの強化版だ。

Futureのscaladocを見てみよう。

  • Trampolineを使っているから、map/flatMapなモナディックな処理をしても一定のスタックしか使わない。
  • scala-2.10のFutureと違って、map/flatMapごとに新しくspawnしないし、ExecutionContextのimplicitも必要ない。

とのことらしい。

やたらscalaのFutureをdisっているので、scalazのFutureはより良いものなのだろう。

使ってみよう

まず確認用の関数を定義しておく。

def calc[A](a: A) = { Thread.sleep(1000); println(s"calc $a"); a }

Future.applyに処理を渡し、runで同期的に実行。(タイムアウトをつける場合はrunFor)

scala> Future(calc(1)).run
calc 1
res31: Int = 1

非同期で実行する場合は、start。

scala> Future(calc(1)).start
res32: scalaz.concurrent.Future[Int] = Suspend(<function0>)
calc 1

明示的だ。

onComplete的なことをしたい場合は、runAsync。

scala> Future(calc(1)).runAsync(i => println(s"i is $i"))
calc 1
i is 1

ここで一旦applyの定義を見てみよう。

/** Create a `Future` that will evaluate `a` using the given `ExecutorService`. */
  def apply[A](a: => A)(implicit pool: ExecutorService = Strategy.DefaultExecutorService): Future[A] = Async { cb =>
    pool.submit { new Callable[Unit] { def call = cb(a).run }}
  }

java.concurrentのを使っている。scalaのFutureと違いとても明確で分かりやすいコードだ。(scalaのFutureは謎に複雑で読むの辛い。Futureに限らないかもだけど...)

処理をつなげてみよう。

mapを使う。

scala> Future(calc(1)).map(i => calc(i + 1)).run
calc 1
calc 2
res22: Int = 2

順番に実行される。この辺の挙動はscalaのFutureと同じく、completeしてから次の処理が続く。

もちろんforもできる。

scala> for { a <- Future(calc(1)); b <- Future(calc(2)) } yield a + b
res39: scalaz.concurrent.Future[Int] = BindAsync(<function1>,<function1>)

scala> .start
res40: scalaz.concurrent.Future[Int] = Suspend(<function0>)

calc 1
calc 2

処理をまとめて受け取る。

Future.sequenceみたいに同時に走らせて結果をまとめるものもあった。

scala> Future.gatherUnordered( Future(calc(1)) :: Future(calc(2)) :: Nil ).run
calc 2
calc 1
res54: List[Int] = List(1, 2)

結果を合わせる。(Monoidのmappend)

scala> Future.futureInstance.aggregate( Future(calc(1)) :: Future(calc(2)) :: Nil ).run
calc 1
calc 2
res159: Int = 3

Future.futureInstanceはNondeterminismのインスタンスだ。

まとめて結果を受け取り、順番通りに取得する。

both: 2つ並列に実行

scala> Future.futureInstance.both(Future(calc(1)), Future(calc(2))).run
calc 1
calc 2
res171: (Int, Int) = (1,2)

mapBoth: 2つ並列に実行後、callback関数を呼ぶ。

scala> Future.futureInstance.mapBoth(Future(calc(1)), Future(calc(2))) { case (a, b) => a + b }.run
calc 2
calc 1
res174: Int = 3

nmap3: 3つ並列に実行後、callback関数を呼ぶ。

scala> Future.futureInstance.nmap3(Future(calc(1)), Future(calc(2)), Future(calc(3))) { case (a, b, c) => a + b + c }.run
calc 1
calc 3
calc 2
res176: Int = 6

nmap6まである。

例外処理は含まない

scalazのFutureは例外処理を含まないので、scalaのFutureのように例外処理を扱いたいときは、Taskを使う。

次はTaskを見るとしよう。

今日はここまで。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6