猫效应(cats-effect)IO单子是如何工作的?

4

我对函数式编程和Scala还不熟悉,最近查看了Cats Effect框架并试图了解IO单子的作用。目前我的理解是,在IO块中编写的代码只是对需要执行的操作进行描述,并且除非使用提供的unsafe方法显式运行它,否则什么也不会发生;此外,这也是一种通过实际不运行代码来使具有副作用的代码具有引用透明性的方式。

我尝试执行下面的片段,只是想试着理解它的含义:

object Playground extends App {
  var out = 10
  var state = "paused"

  def changeState(newState: String): IO[Unit] = {
    state = newState
    IO(println("Updated state."))
  }

  def x(string: String): IO[Unit] = {
    out += 1
    IO(println(string))
  }

  val tuple1 = (x("one"), x("two"))

  for {
    _ <- x("1")
    _ <- changeState("playing")
  } yield ()

  println(out)
  println(state)
}

输出结果如下:

13
paused

我不明白为什么赋值语句 state = newState 不执行,但增加和赋值表达式 out += 1 执行。我是否错过了一些关于如何工作的明显内容?我真的需要帮助。我知道可以使用 unsafe 方法来运行此代码。


2
只是顺便提一下,如果你要进行纯函数式编程,var 在你的代码中就没有位置了。IO Monad 的整个重点在于包含副作用。如果你想要维护这样的可变状态,cats 会为你提供许多选项,例如 Ref - sinanspd
@sinanspd 好的,明白了!我只是想理解一下IO不运行副作用的含义,但只有在包装内才能实现。 - ssmallya
2
这是在执行 IO(...) 时调用的函数:https://github.com/typelevel/cats-effect/blob/d2c1eed51c504681bdf53a2d39b51f9023e5714d/core/shared/src/main/scala/cats/effect/IO.scala#L1141。它采用一个按名称参数的值。你必须知道它是什么以及它的工作原理,才能理解 IO 的工作方式。 - Saskia
1个回答

4
在您的特定示例中,我认为发生的情况是常规命令式Scala代码不受IO单子的影响,它会在Scala规则下正常执行。
当您运行:
for {
  _ <- x("1")
  _ <- changeState("playing")
} yield ()

这将立即调用 x。 这与 IO 单子无关; 这只是 for 推导式的定义方式。 第一步是评估第一语句,以便可以在其上调用 flatMap

正如您观察到的那样,您从未“运行”单子结果,因此无法调用 flatMap 的参数,单子延续,这导致不调用 changeState。 这是特定于 IO 单子的,例如,List 单子的 flatMap 会立即调用该函数(除非它是空列表)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接