像Eithers这样的错误处理单子如何实现参照透明度?

问题描述 投票:1回答:1

通过阅读FP,我对去除副作用的好处的理解是,如果我们所有的函数都是纯的/具有引用透明性(只能在没有副作用的情况下实现),那么我们的函数更容易测试,调试,重用,更模块化。

由于异常是副作用的一种形式,我们需要避免抛出异常。显然我们仍然需要能够在出现问题时终止进程,因此FP使用monads来实现引用透明性和处理异常的能力。

令我困惑的是monad是如何实现这一目标的。假设我有使用scalaz的代码

def couldThrowException: Exception \/ Boolean = ??
val f  = couldThrowException()
val g = couldThrowException()

由于couldThrowException可能会返回一个例外,因此没有任何关于fg将会相同的人。 f可能是\/-(true)g-\/(NullPointerException)。由于couldThrowException可以使用相同的输入返回不同的值,因此它不是纯函数。使用monads是不是要保持我们的功能纯粹?

scala functional-programming monads scalaz
1个回答
5
投票

在给定相同输入的情况下,f()g()应评估相同的值。

在纯FP中,没有参数的函数必须在每次调用时都评估为相同的结果。因此,如果你的couldThrowException有时会返回\/-(true),有时会返回-\/(NullPointerException),那么它不是纯粹的FP。

如果couldThrowException接受参数,则返回Either更有意义。如果它是纯函数,它将具有参考透明度,因此一些输入将始终导致\/-(true),并且一些输入将始终导致-\/(NullPointerException)

在Scala中,您可能正在使用一种不纯粹的功能,而不是引用透明的功能。也许这是一个Java类。也许它使用Scala的一部分并不纯粹。

但我猜你有兴趣在纯FP世界和不纯的图书馆之间架起桥梁。一个典型的例子就是IO。 println可能由于各种原因而失败 - 权限,文件系统已满等。

在FP中处理此问题的经典方法是使用IO函数,该函数将“world”状态作为输入参数,并返回IO调用的结果和新的“world”状态。状态可以是由不纯的语言中的库代码支持的“假”值,但它意味着每次调用函数时,您传递的是不同的状态,因此它是引用透明的。

通常,monad用于封装“世界”。

通过阅读Haskell的IO monad,你可以找到很多关于这种方法的信息。

Core Scala IO并不是完全纯粹的,所以println可能会抛出异常,因此,正如您所发现的那样,并不是完全透明的。 Scalaz提供类似于Haskell的IO monad。

需要注意的一点是,因为它让许多初学者蠢蠢欲动:没有什么关于需要monad的“世界”方法,而且当第一次学习monad是什么以及为什么他们是monad时,IO并不是最容易看到的monad有用。

© www.soinside.com 2019 - 2024. All rights reserved.