Cats Effect 的 IO.suspend 函数到底有什么作用?
What does Cats Effect's IO.suspend function really do?
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, a: Long, b: Long): IO[Long] =
if (n > 0)
fib(n -1, b, a + b)
else
IO.pure(a)
一个懒,一个急。
def printFibIfNeeded(n: Int, shouldPrint: Boolean) = {
val fibResult = fib(n, 0, 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, a: Long, b: Long): IO[Long] = {
println("evaluating")
if (n > 0)
fib(n -1, b, a + b)
else
IO.pure(a)
}
然后调用 fib
创建一个 IO 值而不对它求值,你会看到区别。
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, a: Long, b: Long): IO[Long] =
if (n > 0)
fib(n -1, b, a + b)
else
IO.pure(a)
一个懒,一个急。
def printFibIfNeeded(n: Int, shouldPrint: Boolean) = {
val fibResult = fib(n, 0, 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, a: Long, b: Long): IO[Long] = {
println("evaluating")
if (n > 0)
fib(n -1, b, a + b)
else
IO.pure(a)
}
然后调用 fib
创建一个 IO 值而不对它求值,你会看到区别。