我最近看了一个关于如何提出 IO monad 的视频,谈话是在 scala 中进行的。我实际上想知道让函数返回 IO[A] 有什么意义。包裹在 IO 对象中的 lambda 表达式就是突变,在某个更高的点上,它们必须被观察到,我的意思是被执行,以便发生一些事情。你不只是把问题推到树上更高的地方吗?
我能看到的唯一好处是它允许延迟评估,从某种意义上说,如果您不调用 unsafePerformIO 操作,则不会发生副作用。另外我猜该程序的其他部分可以使用/共享代码并决定何时发生副作用。
我想知道这就是全部吗?可测试性有什么优势吗?我假设不是因为您必须观察某种否定这一点的效果。如果您使用特征/接口,您可以控制依赖关系,但不能控制对这些依赖关系产生影响的时间。
我将以下示例放在代码中。
case class IO[+A](val ra: () => A){
def unsafePerformIO() : A = ra();
def map[B](f: A => B) : IO[B] = IO[B]( () => f(unsafePerformIO()))
def flatMap[B](f: A => IO[B]) : IO[B] = {
IO( () => f(ra()).unsafePerformIO())
}
}
case class Person(age: Int, name: String)
object Runner {
def getOlderPerson(p1: Person,p2:Person) : Person =
if(p1.age > p2.age)
p1
else
p2
def printOlder(p1: Person, p2: Person): IO[Unit] = {
IO( () => println(getOlderPerson(p1,p2)) ).map( x => println("Next") )
}
def printPerson(p:Person) = IO(() => {
println(p)
p
})
def main(args: Array[String]): Unit = {
val result = printPerson(Person(31,"Blair")).flatMap(a => printPerson(Person(23,"Tom"))
.flatMap(b => printOlder(a,b)))
result.unsafePerformIO()
}
}
你可以看到效果是如何延迟到 main 的,我认为这很酷。我是在从视频中了解到这一点后想到的。
我的实施是否正确,我的理解是否正确。
我还想知道是否要获得里程,它应该与 ValidationMonad 结合使用,如 ValidationMonad[IO[Person]] 这样我们可以在发生异常时短路吗?请思想。
布莱尔