[英]Confused about cats-effect Async.memoize
我對貓效應相當陌生,但我想我已經掌握了它。 但是我遇到了一種情況,我想記住 IO 的結果,但它沒有按照我的預期進行。
我要記憶的函數是轉換String => String,但是轉換需要網絡調用,所以實現為函數String => IO[String]。 在非 IO 世界中,我只是簡單地保存調用的結果,但定義函數實際上無法訪問它,因為它直到稍后才會執行。 如果我保存構造的 IO[String],它實際上不會有幫助,因為該 IO 每次使用時都會重復網絡調用。 因此,我嘗試使用 Async.memoize,其中包含以下文檔:
懶洋洋地記住 f。 每次綁定返回的 F[F[A]] 時,效果 f 最多執行一次(第一次綁定內部 F[A] 時)。
我對 memoize 的期望是一個函數,它只對給定的輸入執行一次,並且返回的 IO 的內容只計算一次; 換句話說,我希望得到的 IO 就像 IO.pure(result) 一樣,除了第一次。 但這似乎不是正在發生的事情。 相反,我發現雖然被調用的函數本身只執行一次,但每次仍會評估 IO 的內容——這與我試圖天真地保存和重用 IO 時會發生的情況完全一樣。
我構建了一個示例來顯示問題:
def plus1(num: Int): IO[Int] = {
println("foo")
IO(println("bar")) *> IO(num + 1)
}
var fooMap = Map[Int, IO[IO[Int]]]()
def mplus1(num: Int): IO[Int] = {
val check = fooMap.get(num)
val res = check.getOrElse {
val plus = Async.memoize(plus1(num))
fooMap = fooMap + ((num, plus))
plus
}
res.flatten
}
println("start")
val call1 = mplus1(2)
val call2 = mplus1(2)
val result = (call1 *> call2).unsafeRunSync()
println(result)
println(fooMap.toString)
println("finish")
這個程序的輸出是:
start
foo
bar
bar
3
Map(2 -> <function1>)
finish
盡管 plus1 函數本身只執行一次(打印了一個“foo”),但是包含在 IO 中的輸出“bar”被打印了兩次,而我希望它也只打印一次。 (在將 Async.memoize 返回的 IO 存儲到地圖中之前,我也嘗試將其展平,但這並沒有多大作用)。
考慮以下示例
鑒於以下輔助方法
def plus1(num: Int): IO[IO[Int]] = {
IO(IO(println("plus1")) *> IO(num + 1))
}
def mPlus1(num: Int): IO[IO[Int]] = {
Async.memoize(plus1(num).flatten)
}
讓我們構建一個對plus1(1)
求值兩次的程序。
val program1 = for {
io <- plus1(1)
_ <- io
_ <- io
} yield {}
program1.unsafeRunSync()
這會產生兩次打印plus1
的預期輸出。
如果您執行相同操作,而是使用mPlus1
方法
val program2 = for {
io <- mPlus1(1)
_ <- io
_ <- io
} yield {}
program2.unsafeRunSync()
它只會打印plus1
一次以確認記憶正在起作用。
memoization 的訣竅是它應該只評估一次以獲得所需的效果。 現在考慮以下突出顯示它的程序。
val memIo = mPlus1(1)
val program3 = for {
io1 <- memIo
io2 <- memIo
_ <- io1
_ <- io2
} yield {}
program3.unsafeRunSync()
並將其輸出plus1
兩倍io1
和io2
分別memoized。
對於您的示例, foo
被打印一次,因為您正在使用地圖並在未找到時更新該值,並且這只發生一次。 該bar
是印刷每當IO
為你失去通過調用記憶化效果評估res.flatten
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.