【问题标题】:Cats-effect and the IO monad猫效应和 IO 单子
【发布时间】:2017-11-18 01:50:22
【问题描述】:

我一直在尝试掌握 IO monad,这很有意义。如果我没记错的话,目标是将副作用的描述和实际执行分开。如下例所示,Scala 有一种方法可以获取不透明的环境变量。出现了两个问题。

问题 1:这个是否具有引用透明性

问题 2:如何正确(基于单元/属性)测试这个?无法检查是否相等,因为它将检查内存引用并且无法检查内部函数,因为如果我没记错的话,函数比较是不可能的。但是,我不想在我的单元测试中运行实际的副作用。另外,这是设计错误还是 IO monad 的误用?

case class EnvironmentVariableNotFoundException(message: String) extends Exception(message)

object Env {
  def get(envKey: String): IO[Try[String]] = IO.unit.flatMap((_) => IO.pure(tryGetEnv(envKey)))

  private[this] def tryGetEnv(envKey: String): Try[String] =
    Try(System.getenv(envKey))
      .flatMap(
        (x) =>
          if (x == null) Failure(EnvironmentVariableNotFoundException(s"$envKey environment variable does not exist"))
          else Success(x)
      )
}

【问题讨论】:

    标签: scala functional-programming monads scala-cats


    【解决方案1】:

    最好使用IO 来包装程序中来自不纯源的值,就像示例中的系统调用一样。这将返回一个IO[A],内容为“我能够通过不纯的方式获得A”。此后,您可以通过mapflatMap 等使用作用于A 的纯/引用透明函数。

    这导致两个答案。我想问一下,您要测试它的什么属性?

    查看该代码,我注意到tryGetEnv 中的flatMap 可能足够复杂,需要进行测试。您可以通过将此逻辑提取到纯函数中来做到这一点。您可以(例如)重新编写它,以便有一个返回 IO[String] 的函数,然后编写一个(经过测试的)函数将其转换为您想要的类型。

    IO 完全按照您说的做,但这明确不包括使代码具有引用透明性!如果您想在这里测试实际的副作用,您可以考虑将 System 作为参数传递并模拟它以进行测试,就像在不使用 IO 的程序中一样。

    因此,总而言之,我会考虑创建一个 minimal 函数来调用 System 以创建 IO[A](在本例中为 IO[Try[String]])。您可能会选择通过模拟来测试这个最小功能,但前提是您觉得这样做是在增加价值。围绕这一点,您可以编写带有A 的函数,并通过将纯值传递给这些函数来测试它们。请记住,mapIO 上的签名如下,而这里的f 是一个纯(可测试)函数!

    sealed abstract class IO[+A] {
    
      def map[B](f: A => B): IO[B]
                 ^ f is a pure function!
                   test it by passing A values and verifying the Bs
    

    极端而言,这种模式鼓励您仅在程序的最边缘创建IO 值(例如,您的main 函数)。然后可以从纯函数创建程序的其余部分,这些纯函数作用于程序运行时来自 IO 类型的值。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-03-21
      • 1970-01-01
      • 1970-01-01
      • 2011-10-17
      • 2013-12-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多