了解Scala中的IO monad

St.*_*rio 6 io monads scala

我正在学习scalaz,现在我正在努力理解IOmonad 的观点.我读了这篇关于IO monads的文章,并尝试自己运行最简单的例子:

val io = println("test").pure[IO]
println("before")
io.unsafePerformIO()
Run Code Online (Sandbox Code Playgroud)

是的,它按预期工作.它打印

before
test
Run Code Online (Sandbox Code Playgroud)

但我无法抓住IO monad的要点.诀窍是什么?除了我所引用的文章中指定的"维护替代".

现在,这种替代似乎并不太有用.你能解释一下吗?

从我能想到的.想象一下,我有一些特质:

trait Reader{
    def read(): List[Int]
}

trait Writer[T]{
    def write(t: T): Unit
}
Run Code Online (Sandbox Code Playgroud)

所以我有一个能读取monadic值的读者(List在我的例子中).然后我需要在其他地方的容器中写入所有值,对它们执行一些转换.IO monad在这种情况下有用吗?

Yuv*_*kov 2

IO 就在我们身边,它使程序变得真正有用​​,因为我们不能整天计算纯表达式。

IO monad 试图解决使 IO 操作变得“不纯粹”的问题,例如从网络等不纯粹的来源获取数据。

IO 本身并不是引用透明的。考虑一个返回 的方法Unitprintln例如。让我们尝试println仅替换Unit(或()),我们不会得到相同的值,不是吗?因为println有打印到控制台的效果。

使用你的例子,想象一下:

def write(t: T): Unit
Run Code Online (Sandbox Code Playgroud)

write如果我们替换为 会发生什么()?嗯,写入外部源会产生影响。然而,如果我们使用IO[Unit],我们就不会破坏替换。

要真正解决您的问题,我们需要将Listmonad 与IOmonad 结合起来,并且我们知道 monad 不能组合。我们需要对 Monad Transformers 使用一些技巧:

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()
Run Code Online (Sandbox Code Playgroud)

这并不是很漂亮,而且可能不像调用一个有效的返回操作那么直接Unit,但我确信有比我在这里为了演示目的而创建的更好的方法来解决 monad 组合问题。

  • 如果你有“IO[List[T]]”,那么你需要一个“ListT”转换器,scalaz 确实有。 (2认同)