TimedFuture
エフェクトは Scala の
Future
の薄いラッパーだ。唯一追加された機能は組み込みのタイムアウトであり、ScheduledExecutionContext
に渡されることでサポートされる。注意することとして、Future
はすでに実行されている計算を表しているので、TimedFuture
は
Future
を返す関数である。つまり、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
次に、計算を始めるために、Scheduler
と
ExecutionContext
を渡す必要がある。
implicit val scheduler = ExecutorServices.schedulerFromGlobalExecutionContext
import org.atnos.eff.syntax.future._
Await.result(action.runOption.runSequential, 1 second)
> Some(1)
他の Future
or
Task
エフェクトを使うこともできる。
twitter
Future を使うなら依存関係に
eff-twitter
を追加し、org.atnos.eff.addon.twitter.future._
をインポートする
scalaz
Task を使うなら依存関係に
eff-scalaz
を追加し、org.atnos.eff.addon.scalaz.task._
をインポートする
monix
Task を使うなら依存関係に
eff-monix
を追加し、org.atnos.eff.addon.monix.task._
をインポートする
runAsync
メソッドを呼ぶための対応するシンタックスのインポートもある。
twitter
:
org.atnos.eff.syntax.addon.twitter.future._
scalaz
:
org.atnos.eff.syntax.addon.scalaz.task._
monix
:
org.atnos.eff.syntax.addon.monix.task._
Future
と
Task
の計算はメモ化しておいて、コストの高い計算が何度も実行されるのを避けることもできる。それぞれこうする。r
futureMemo/taskMemo
オペレーターを(ミュータブルな)キャッシュと使う
futureMemoized/taskMemoized
オペレーターを
Memoized
エフェクトと使う(キャッシュをあとで提供する必要がある)
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