Mau*_*aaf 5 monads functional-programming scala scala-cats
我已经尝试掌握 IO monad 一段时间了,这是有道理的。如果我没记错的话,目标是将副作用的描述和实际执行分开。如下例所示,Scala 有一种方法可以获取非引用透明的环境变量。出现了两个问题。
问题一:这个是参照透明的吗
问题 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)
)
}
Run Code Online (Sandbox Code Playgroud)
最好用来IO包装程序中来自不纯源的值,就像示例中的系统调用一样。这将给你返回一个IO[A]内容,内容为“我能够A通过不纯的手段获得一个”。此后,您可以使用作用于 、 via 等的纯/引用透明A函数。mapflatMap
这导致两个答案。我想问一下,你想测试这个的什么属性?
查看该代码,我注意到flatMapintryGetEnv可能足够复杂以保证测试。您可以通过将此逻辑提取到纯函数中来做到这一点。您可以(例如)重写它,以便有一个函数返回一个IO[String],然后编写一个(经过测试的)函数,将其转换为您想要的类型。
IO 完全按照您所说的操作,但这明确不包括使代码引用透明!如果您想在这里测试实际的副作用,您可以考虑将 System 作为参数传递并模拟它进行测试,就像在不使用IO.
总之,我会考虑创建一个最小的函数来调用 System创建一个IO[A](在本例中为 an IO[Try[String]])。您可以选择通过模拟来测试这个最小的函数,但前提是您认为这样做可以增加价值。围绕这一点,您可以编写接受 an 的函数A,并通过将纯值传递给这些函数来测试它们。map请记住, on的签名IO如下,这里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
Run Code Online (Sandbox Code Playgroud)
IO极端地说,这种模式鼓励您仅在程序的最边缘(例如您的函数)创建值main。然后,可以从纯函数创建程序的其余部分,这些函数作用于程序运行时来自 IO 类型的值。