Eval
概要
式の評価を制御するモナドインスタンス
map/flatMapはトランポリンされている→スタックセーフ
valueメソッドが呼び出されるまでは実行されない(計算の計画を設計するだけ)
再帰的な構造を強制的にスタックセーフにするために使えるデータ型
foldRight
table:評価
メモ化 メモ化しない
正格(eager) val, Now
遅延(lazy) lazy val, Later def, Always
Scala
Eval
Now(call-by-value) | Later(call-by-name) | Always(call-by-need)
code:scala
val ans = for {
a <- Eval.now{ println("Calculating A"); 40 }
b <- Eval.always{ println("Calculating B"); 2 }
} yield {
println("Adding A and B")
a + b
}
// Calculating A
// ans: EvalInt = cats.Eval$$anon$4@2d0f2cbf ans.value // first access
// Calculating B
// Adding A and B
// res17: Int = 42 // first access
ans.value // second access
// Calculating B
// Adding A and B
// res18: Int = 42
code:scala
val saying = Eval
.always{ println("Step 1"); "The cat" }
.map{ str => println("Step 2"); s"$str sat on" }
.memoize
.map{ str => println("Step 3"); s"$str the mat" }
// saying: EvalString = cats.Eval$$anon$4@ca01c64 saying.value // first access
// Step 1
// Step 2
// Step 3
// res19: String = "The cat sat on the mat" // first access
saying.value // second access
// Step 3
// res20: String = "The cat sat on the mat"
フィボナッチをスタックセーフにする
/icons/point.iconEval.deferを使って再帰する
code:scala
def factorial(n: BigInt): BigInt =
if(n == 1) n else n * factorial(n - 1)
factorial(50000)
// java.lang.StackOverflowError
def factorial(n: BigInt): EvalBigInt = if(n == 1) {
Eval.now(n)
} else {
Eval.defer(factorial(n - 1).map(_ * n))
}
factorial(50000).value
// res: A very big value