3

我对猫的影响还很陌生,但我想我已经掌握了它。但是我遇到了一种情况,我想记住 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 存储到地图之前将其展平,但这并没有多大作用)。

4

1 回答 1

3

考虑以下示例

给定以下辅助方法

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分别记忆。

至于您的示例,foo因为您正在使用地图并在未找到该值时更新该值,所以会打印一次,而这只发生一次。bar每次评估时都会打印 ,因为IO您通过调用 失去记忆效果res.flatten

于 2020-10-25T09:24:04.163 回答