Scala 猫的作用是什么;s IO.suspend函数真的可以吗?
cats effect的IO.suspend的作用是什么?它为什么有用?有文档记录,但并不完全清楚 提供了以下用例:Scala 猫的作用是什么;s IO.suspend函数真的可以吗?,scala,functional-programming,scala-cats,cats-effect,Scala,Functional Programming,Scala Cats,Cats Effect,cats effect的IO.suspend的作用是什么?它为什么有用?有文档记录,但并不完全清楚 提供了以下用例: 导入cats.effect.IO def fib(n:Int,a:Long,b:Long):IO[Long]= 暂停{ 如果(n>0) fib(n-1,b,a+b) 其他的 IO.pure(a) } 举个例子,我为什么要使用上面的函数,而不是下面的类似函数 导入cats.effect.IO 导入scala.annotation.tailrec @泰勒克 def fib(n:In
导入cats.effect.IO
def fib(n:Int,a:Long,b:Long):IO[Long]=
暂停{
如果(n>0)
fib(n-1,b,a+b)
其他的
IO.pure(a)
}
举个例子,我为什么要使用上面的函数,而不是下面的类似函数
导入cats.effect.IO
导入scala.annotation.tailrec
@泰勒克
def fib(n:Int,a:Long,b:Long):IO[Long]=
如果(n>0)
fib(n-1,b,a+b)
其他的
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
将只是一个计算公式,如果应该打印,则不会运行该计算错误
在第二个示例中,您已经计算了该值,并将其包装为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进行评估,否则不会创建此未来
。所以用户是安全的,除非我们明确地将这个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)
}
及
然后调用
fib
创建一个IO值而不计算它,您将看到不同之处。其中一个是懒惰的,另一个是渴望的
def printFibIfNeeded(n: Int, shouldPrint: Boolean) = {
val fibResult = fib(n, 0, 1)
if (shouldPrint) fibResult.map(r => println(r))
else IO.unit
}
如果使用suspend
,则fibResult
将只是一个计算公式,如果应该打印,则不会运行该计算错误
在第二个示例中,您已经计算了该值,并将其包装为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进行评估,否则不会创建此未来
。所以用户是安全的,除非我们明确地将这个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)
}
及
然后调用
fib
创建一个IO值,而不进行求值,您将看到差异。在您的第二个代码中,如果(n>0)分支被急切地求值,这正是您想要停止的(因为这个分支是计算的全部)。另一根树枝很轻。因此,在您的第二个代码中,IO
被用于纯粹的装饰目的,您完全可以不使用它。至于为什么暂停有用?
,它围绕着能够将“过程”
表示为数据的基本范例展开,因此,您可以对其进行分析,以找出更好的优化、修剪不必要的分支等等。然后您最终运行经过修剪和优化的“进程”
,而不是“编写的”
。另一个原因是保持引用的透明性,这是函数编程和数学推理的基础。另外,请记住,我们正在将“进程”
转换为数据,这意味着您正在将进程从嵌套函数调用提升为嵌套数据结构。。。这再次意味着我们不再担心烟囱安全。您可能还记得,并不是所有递归计算都可以进行尾部递归(或者需要一些复杂的逻辑更改,或者需要使用诸如连续体之类的东西),这种方法允许我们在第二个代码中使用具有堆栈安全性的非尾部递归实现(因为我们不使用堆栈进行递归),if(n>0)
分支被急切地评估,这正是您想要停止的(因为这个分支是计算的全部)。另一根树枝很轻。因此,在您的第二个代码中,IO
被用于纯粹的装饰目的,您完全可以不使用它。至于为什么暂停有用?
,它围绕着能够将“过程”
表示为数据的基本范例展开,因此,您可以对其进行分析,以找出更好的优化、修剪不必要的分支等等。然后您最终运行经过修剪和优化的“进程”
,而不是“编写的”
。另一个原因是保持引用的透明性,这是函数编程和数学推理的基础。另外,请记住,我们正在将“进程”
转换为数据,这意味着您正在将进程从嵌套函数调用提升为嵌套数据结构。。。这再次意味着我们不再担心烟囱安全。您可能还记得,并不是所有递归计算都可以进行尾部递归(或者需要一些复杂的逻辑更改,或者需要使用连续性之类的东西),这种方法允许我们使用具有堆栈安全性的非尾部递归实现(因为我们不使用堆栈进行递归)