簡體   English   中英

對貓效應 Async.memoize 感到困惑

[英]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兩倍io1io2分別memoized。

對於您的示例, foo被打印一次,因為您正在使用地圖並在未找到時更新該值,並且這只發生一次。 bar是印刷每當IO為你失去通過調用記憶化效果評估res.flatten

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM