TimedFuture

TimedFuture エフェクトは Scala の Future の薄いラッパーだ。唯一追加された機能は組み込みのタイムアウトであり、ScheduledExecutionContext に渡されることでサポートされる。注意することとして、Future はすでに実行されている計算を表しているので、TimedFutureFuture を返す関数である。つまり、Future を Eff にわたす前に計算を始めたいなら、その Future がいつ始まっているかは予測がしにくくなる。

Now, let’s create some TimedFuture effects:

import org.atnos.eff._
import org.atnos.eff.future._
import org.atnos.eff.syntax.all._

import scala.concurrent._, duration._
import scala.concurrent.ExecutionContext.Implicits.global

type R = Fx.fx2[TimedFuture, Option]

val action: Eff[R, Int] =
  for {
    // 新しい値を即座に作る
    a <- Eff.pure[R, Int](1)

    // 同じスレッドプールで値をあとで評価し、評価が完了したら処理を続ける
    b <- futureDelay[R, Int](1)
  } yield b

次に、計算を始めるために、SchedulerExecutionContext を渡す必要がある。


implicit val scheduler = ExecutorServices.schedulerFromGlobalExecutionContext
import org.atnos.eff.syntax.future._

Await.result(action.runOption.runSequential, 1 second)

> Some(1)

他の Future or Task エフェクトを使うこともできる。

runAsync メソッドを呼ぶための対応するシンタックスのインポートもある。

FutureTask の計算はメモ化しておいて、コストの高い計算が何度も実行されるのを避けることもできる。それぞれこうする。r

import cats.implicits._
import org.atnos.eff._, future._, all._
import org.atnos.eff.syntax.all._
import org.atnos.eff.syntax.future._
import scala.concurrent._, duration._
import scala.concurrent.ExecutionContext.Implicits.global

var i = 0

def expensive[R :_Future :_memo]: Eff[R, Int] =
  futureMemoized[R, Int]("key", futureDelay[R, Int] { i += 1; 10 * 10})

type S = Fx.fx2[Memoized, TimedFuture]

implicit val scheduler = ExecutorServices.schedulerFromGlobalExecutionContext

val futureMemo: Future[Int] =
  (expensive[S] >> expensive[S]).runFutureMemo(ConcurrentHashMapCache()).runSequential

Await.result(futureMemo, 1.second)

"there is only one invocation" <==> (i === 1)

> there is only one invocation <=> true