像 Eithers 这样的错误处理单子如何实现引用透明性?
How do error handling monads like Eithers achieve referential transparency?
通过阅读 FP,我对消除副作用的好处的理解是,如果我们所有的函数都是 pure/have 引用透明(只有在没有副作用的情况下才能实现的东西),那么我们的函数更易于测试、调试、重用,并且更加模块化。
由于异常是一种副作用,我们需要避免抛出异常。显然我们仍然需要能够在出现问题时终止进程,因此 FP 使用 monad 来实现引用透明性和处理异常的能力。
我感到困惑的是 monad 究竟是如何实现这一点的。假设我有这段代码使用 scalaz
def couldThrowException: Exception \/ Boolean = ??
val f = couldThrowException()
val g = couldThrowException()
由于 couldThrowException
可能 return 例外,因此无法保证 f
和 g
相同。 f
可以是 \/-(true)
而 g
可以是 -\/(NullPointerException)
。由于 couldThrowException
可以 return 具有相同输入的不同值,因此它不是纯函数。使用 monad 不是为了让我们的函数保持纯净吗?
f()
和 g()
应该 计算出相同的值,给定相同的输入。
在纯 FP 中,没有参数的函数在每次调用时都必须计算出相同的结果。因此,如果您的 couldThrowException
有时 return 有时 \/-(true)
有时 -\/(NullPointerException)
.
这不是纯粹的 FP
如果 couldThrowException
接受参数,则 return Either 更有意义。如果它是一个纯函数,它将具有引用透明性,因此某些输入将始终导致 \/-(true)
而某些输入将始终导致 -\/(NullPointerException)
.
在 Scala 中,您可能会使用 not 纯函数,并且 not 引用透明。也许是 Java class。也许它使用了不纯的 Scala 的一部分。
但我猜您对在纯 FP 世界和非纯库之间架起桥梁感兴趣。一个 classic 的例子是 IO。 println
可能由于各种原因而失败 - 权限、文件系统已满等。
在 FP 中处理此问题的 classic 方法是使用一个 IO 函数,该函数将 "world" 状态作为输入参数,并且 returns 都是IO调用,以及新的"world"状态。状态可以是一个 "fake" 值,由不纯语言的库代码支持,但这意味着每次调用该函数时,您都会传递一个不同的状态,因此它是引用透明的。
通常,monad 用于封装 "world"。
您可以通过阅读 Haskell 的 IO monad 了解很多关于这种方法的信息。
Core Scala IO 并非完全纯净,因此 println
可能会抛出异常,因此,如您所见,它并不是完全引用透明的。 Scalaz 提供了一个类似于 Haskell 的 IO monad。
需要注意的一件事,因为它会绊倒很多初学者:"World" 方法没有任何需要 monad 的地方,而且 IO 在第一次学习 monad 时并不容易看monad 是什么以及为什么它们有用。
通过阅读 FP,我对消除副作用的好处的理解是,如果我们所有的函数都是 pure/have 引用透明(只有在没有副作用的情况下才能实现的东西),那么我们的函数更易于测试、调试、重用,并且更加模块化。
由于异常是一种副作用,我们需要避免抛出异常。显然我们仍然需要能够在出现问题时终止进程,因此 FP 使用 monad 来实现引用透明性和处理异常的能力。
我感到困惑的是 monad 究竟是如何实现这一点的。假设我有这段代码使用 scalaz
def couldThrowException: Exception \/ Boolean = ??
val f = couldThrowException()
val g = couldThrowException()
由于 couldThrowException
可能 return 例外,因此无法保证 f
和 g
相同。 f
可以是 \/-(true)
而 g
可以是 -\/(NullPointerException)
。由于 couldThrowException
可以 return 具有相同输入的不同值,因此它不是纯函数。使用 monad 不是为了让我们的函数保持纯净吗?
f()
和 g()
应该 计算出相同的值,给定相同的输入。
在纯 FP 中,没有参数的函数在每次调用时都必须计算出相同的结果。因此,如果您的 couldThrowException
有时 return 有时 \/-(true)
有时 -\/(NullPointerException)
.
如果 couldThrowException
接受参数,则 return Either 更有意义。如果它是一个纯函数,它将具有引用透明性,因此某些输入将始终导致 \/-(true)
而某些输入将始终导致 -\/(NullPointerException)
.
在 Scala 中,您可能会使用 not 纯函数,并且 not 引用透明。也许是 Java class。也许它使用了不纯的 Scala 的一部分。
但我猜您对在纯 FP 世界和非纯库之间架起桥梁感兴趣。一个 classic 的例子是 IO。 println
可能由于各种原因而失败 - 权限、文件系统已满等。
在 FP 中处理此问题的 classic 方法是使用一个 IO 函数,该函数将 "world" 状态作为输入参数,并且 returns 都是IO调用,以及新的"world"状态。状态可以是一个 "fake" 值,由不纯语言的库代码支持,但这意味着每次调用该函数时,您都会传递一个不同的状态,因此它是引用透明的。
通常,monad 用于封装 "world"。
您可以通过阅读 Haskell 的 IO monad 了解很多关于这种方法的信息。
Core Scala IO 并非完全纯净,因此 println
可能会抛出异常,因此,如您所见,它并不是完全引用透明的。 Scalaz 提供了一个类似于 Haskell 的 IO monad。
需要注意的一件事,因为它会绊倒很多初学者:"World" 方法没有任何需要 monad 的地方,而且 IO 在第一次学习 monad 时并不容易看monad 是什么以及为什么它们有用。