了解 Scala 中的 IO monad

Understanding IO monad in Scala

我正在学习 scalaz,现在我正试图理解 IO monad 的要点。我阅读了 this article 关于 IO monads 的内容,并试图 运行 我自己的最简单的例子:

val io = println("test").pure[IO]
println("before")
io.unsafePerformIO()

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

before
test

但我无法理解 IO monad 的要点。诀窍是什么?除了我引用的文章中指定的 "maintains substitution" 。

这个替换现在对我来说似乎也不太有用。能解释一下吗?

据我所知。假设我有一些特征:

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

trait Writer[T]{
    def write(t: T): Unit
}

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

IO 无处不在,它使程序真正有用,因为我们不能整天只计算纯表达式。

IO monad 试图解决进行 IO 操作的问题 "unpure",例如从网络等不纯来源获取数据。

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

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

def write(t: T): Unit

如果我们用 () 替换 write 会发生什么?好吧,写入外部源会有效果。但是,如果我们使用 IO[Unit],我们就不会破坏替换。

要实际查看您的问题,我们需要将 List monad 与 IO monad 结合起来,而且我们知道 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()

这不是很漂亮,可能不像调用一个有效的操作返回 Unit 那样直接,但我确信有比我所用的更好的方法来绕过 monad 组合出于演示的目的而在此处创建。