Cats Effect 的 IO.suspend 函数到底有什么作用?

问题描述

cats-effect 的 IO.suspend 有什么作用?它为什么有用?有文档,但不完全清楚。

documentation 给出了以下用例:

import cats.effect.IO

def fib(n: Int,a: Long,b: Long): IO[Long] =
  IO.suspend {
    if (n > 0)
      fib(n - 1,b,a + b)
    else
      IO.pure(a)
  }

举个例子,为什么我要使用上面的,而不是下面的类似功能?

import cats.effect.IO

import scala.annotation.tailrec

@tailrec
def fib(n: Int,b: Long): IO[Long] =
  if (n > 0)
    fib(n -1,a + b)
  else
    IO.pure(a)

解决方法

其中一个是懒惰的,另一个是急切的。

def printFibIfNeeded(n: Int,shouldPrint: Boolean) = {
  val fibResult = fib(n,1)
  if (shouldPrint) fibResult.map(r => println(r))
  else IO.unit
}

如果您使用 suspend,那么 fibResult 将只是一个计算配方,如果 shouldPrint. = false 则不会运行。

在您的第二个示例中,您已经计算了该值并用 IO 包装了它,因此您使您的计算机运行 CPU,即使它最终没有必要。

对于纯计算,它可能看起来像是一种优化,但如果您有副作用怎么办?

def dropAllUsersInDB: Future[Unit]

def eagerDrop = IO.fromFuture(IO.pure(dropAllUsersInDB))

def lazyDrop = IO.fromFuture(IO.suspend(IO.pure(dropAllUsersInDB)))

第一个示例创建了 Future,它将删除所有用户 - 因此无论我们是否将此 IO 组合到我们的程序中,用户都将被删除。

第二个示例在 IO 配方中的某处有 suspend,因此除非对 IO 进行评估,否则不会创建此 Future。所以用户是安全的,除非我们明确地将这个 IO 组合成一些将成为计算一部分的东西。

在您的示例中,如果您这样做,这将变得更加明显:

def fib(n: Int,a: Long,b: Long): IO[Long] =
  IO.suspend {
    println("evaluating")
    if (n > 0)
      fib(n - 1,b,a + b)
    else
      IO.pure(a)
  }

@tailrec
def fib(n: Int,b: Long): IO[Long] = {
  println("evaluating")
  if (n > 0)
    fib(n -1,a + b)
  else
    IO.pure(a)
}

然后调用 fib 来创建一个 IO 值而不计算它,你会看到不同之处。

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...