功能编程如何在看似不可避免的情况下避免状态?

时间:2012-10-28 09:36:11

标签: functional-programming state

假设我们定义一个函数c sum(a, b),函数编程式,它返回其参数的总和。到现在为止还挺好; FP的所有好东西没有任何问题。

现在让我们假设我们在具有动态类型和单例,有状态错误流的环境中运行它。然后我们假设我们传递a和/或b的值sum不是为了处理(即不是数字),而是需要以某种方式指出错误。

但是怎么样?这个功能应该是纯粹的,没有副作用。它如何在不违反全局错误流的情况下插入错误?

1 个答案:

答案 0 :(得分:9)

我所知道的编程语言没有像#34;单例状态错误流"内置,所以你必须做一个。如果您试图以纯粹的功能性风格编写程序,那么您只是不会做出这样的事情。

但是,您可以使用sum函数返回总和或指示错误。实际上,用于执行此操作的类型通常名为Either。然后,您可以轻松地创建一个函数来调用可能返回错误的一大堆计算,并返回在其他计算中遇到的所有错误的列表。这与你所谈论的非常接近;它只是明确地返回而不是全球化。

请记住,当您编写功能性程序时,问题是"如何制作具有我想要的行为的程序?"不是,"我如何复制另一种编程风格的特定方法?"。 A"全局有状态错误流"是意味着而不是结束。您不能在纯函数样式中拥有全局有状态错误流,否。但问问自己,您使用全局有状态错误流来实现实现;无论它是什么,你都可以在函数式编程中实现 ,而不是使用相同的机制。

询问纯函数式编程是否可以实现依赖于副作用的特定技术,就像询问如何在面向对象编程中使用汇编技术。 OO提供了不同的工具供您解决问题;限制自己使用这些工具来模拟不同的工具集并不是一种有效的方法。


回复评论:如果您希望实现并使用错误流将错误消息记录到终端,那么是的,在某种程度上代码将会发生必须做IO才能做到这一点。 1

打印到终端就像任何其他IO一样,没有什么特别之处,这使得它值得单挑作为一个国家似乎特别不可避免的情况。因此,如果这会将您的问题转变为"纯功能程序如何处理IO?",那么毫无疑问,SO上有许多重复的问题,更不用说许多博客文章和教程正是针对该问题。对于纯编程语言的实现者和用户来说,这不是一个突然的惊喜,这个问题已经存在了几十年,并且已经有一些相当复杂的想法被放入答案中。

在不同语言中采用了不同的方法(Haskell中的IO monad,Mercury中的独特模式,Haskell历史版本中的请求和响应的惰性流等等)。基本思想是提出一个可以通过纯代码操作的模型,并将模型的操作与语言实现中的实际不纯操作联系起来。这使您可以保持纯度的好处(适用于纯代码但不适用于一般不纯的代码的证明仍将适用于使用纯IO模型的代码。)

纯粹的模型必须经过精心设计,以便您无法实际使用在实际IO方面没有意义的任何内容。例如,Mercury通过让您编写程序来执行IO,就像您将宇宙的当前状态作为额外参数传递一样。这个纯模型准确地表示了依赖于并影响程序外部的Universe的操作行为,但仅限于系统中任何时候只有一个Universe状态,从头到尾贯穿整个程序。 。

中有一些限制
  1. 类型io是抽象的,因此无法构造该类型的值;获得一个的唯一方法是从你的来电者传递一个。语言实现将io值传递给main谓词,以完成所有操作。
  2. 声明传递给io的{​​{1}}值的模式,使其唯一。这意味着您无法执行可能导致重复的事情,例如将其放入容器或将相同的main值传递给多个不同的调用。唯一模式确保您只能将io值分配给也使用唯一模式的谓词,并且一旦您将值传递给#34; dead"并且无法通过其他任何地方。

  3. 1 请注意,即使在命令式程序中,如果您的错误日志记录系统返回错误消息流,然后只是实际决定将它们打印到接近的位置,您将获得很大的灵活性。程序的最外层。如果您的日志调用是直接直接写入输出,那么我可以想到的一些事情在我的脑海中变得更加困难:

    • 通过检查是否发生任何错误,推测性地执行计算并查看是否失败
    • 将多个高级系统组合到一个系统中,向日志添加标签以区分每个系统
    • 仅在出现错误消息时才发出调试和信息日志消息(因此,当没有要调试的错误时输出是干净的,并且当有错误时,输出很详细)