了解Scala中的IO monad

时间:2017-11-21 13:42:48

标签: scala io monads

我正在学习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在这种情况下是否有用?

1 个答案:

答案 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组合而不是我和为了演示而在这里创建了。