我正在学习scalaz,现在我正在努力理解IO
monad的观点。我阅读了关于IO monads的this article并尝试自己运行最简单的例子:
val io = println("test").pure[IO]
println("before")
io.unsafePerformIO()
是的,它按预期工作。它打印
before
test
但我无法理解IO monad的要点。诀窍是什么?除了我所引用的文章中指定的“维护替代”。
现在我的替换似乎并不太有用。你能解释一下吗?
从我能想到的。想象一下,我有一些特质:
trait Reader{
def read(): List[Int]
}
trait Writer[T]{
def write(t: T): Unit
}
所以我有一个读取器可以读取monadic值(在我的情况下为List
)。然后我需要在其他地方的容器中写入所有值,对它们执行一些转换。 IO monad在这种情况下是否有用?
答案 0 :(得分:2)
IO就在我们身边,这是使程序真正有用的原因,因为我们不能整天计算纯表达式。
IO monad尝试解决使IO操作" unure"等问题,例如从不纯的来源(如网络)获取数据。
IO,就其本身而言,不是指称透明的。考虑返回Unit
的方法,例如println
。让我们尝试将println
替换为Unit
(或()
),我们不会获得相同的价值,是吗?因为println
具有打印到控制台的效果。
使用您的示例,想象一下:
def write(t: T): Unit
如果我们用write
替换()
会怎样?那么,写入外部源会产生影响。但是,如果我们使用IO[Unit]
,我们就不会打破替换。
要实际查看您的问题,我们需要将List
monad与IO
monad合并,而且我们知道monad不会撰写。我们需要借助Monad变形金刚的一些技巧:
import cats._
import cats.data.Nested
import cats.effect.IO
import cats.implicits._
val nested = Nested(IO.pure(reader.read()))
val res: Nested[IO, List, IO[Unit]] =
Functor[Nested[IO, List, ?]].map(nested)(i => writer.write(i))
val ioResult = for {
listOfIO <- res.value
flattened <- Applicative[IO].sequence(listOfIO)
} yield flattened
ioResult.unsafeRunSync()
这不是很漂亮,可能不像调用返回Unit
的有效操作那样直截了当,但我确信有更好的方法来解决monad组合而不是我和为了演示而在这里创建了。