在纯函数式编程中可能会出现副作用 [英] Are side-effects possible in pure functional programming

查看:133
本文介绍了在纯函数式编程中可能会出现副作用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我现在一直在尝试围绕函数式编程进行研究。我查了一下lambda演算,LISP,OCML,F#甚至组合逻辑,但我的主要问题是如何做需要副作用的事情(如与用户交互,与远程服务进行通信,甚至处理模拟使用随机抽样)而不违反纯函数编程的基本前提,即对于给定输入,输出是确定性的?我希望我有道理,如果不是,我欢迎任何尝试正确教育我的人。在大多数意义上,大多数现实世界的函数式编程并不是纯的,所以一半的答案是你的问题是你通过放弃纯度来做到这一点。也就是说, 是替代品。



在纯粹的纯粹意义上,整个程序代表一个或多个参数,返回一个值。如果你眯起眼睛并挥动双手,你可以声明所有的用户输入都是函数的参数的一部分,并且所有的输出都是返回值的一部分,然后进行一些操作,以便它只做实际的I / O按需。

一个类似的观点是声明函数的输入是外部世界的整个状态,并且评估该函数返回一个新的,修改后的世界状态。在这种情况下,使用世界状态的程序中的任何功能显然都从确定性中解脱出来,因​​为没有两个程序评估的外部世界会完全相同。



如果你想在纯lambda演算中编写一个交互式程序(或者类似的东西,比如深奥的语言Lazy K),那么在概念上你会怎么做。



更实际的问题是,当输入被用作函数的参数时,问题归结为确保I / O按正确的顺序发生。这个问题的纯粹解决方案的一般结构是函数组合。例如,假设你有三个I / O函数,并且你想以特定顺序调用它们。如果你做了像 RunThreeFunctions(f1,f2,f3)之类的东西,那么就没有什么可以决定它们的评估顺序了。另一方面,如果让每个函数另一个函数作为参数,你可以像这样链接它们: f1(f2(f3())),在这种情况下,你知道 f3 将首先评估,因为 f2 的评估取决于其值。 [编辑:另请参阅以下关于懒惰与渴望评估的评论。这很重要,因为在非常纯粹的环境下,懒惰评估实际上非常普遍;例如,在纯lambda演算中递归的标准实现在急切的评估中是非终止的。]

再次,为了在lambda演算中编写一个交互式程序,这就是你'd可能会这样做。如果你想要一些实际可用于编程的东西,你可能希望将函数组合部分与函数的概念结构结合起来,并返回代表世界状态的值,并创建一些更高阶的抽象来处理流水线的世界状态之间的价值之间的I / O功能,理想情况下也保持世界国家,以强制执行严格的线性 - 在这一点上,你几乎重新Haskell的 IO Monad。



希望这不会让你感到困惑。


I have been trying to wrap my head around functional programming for a while now? I have looked up lambda calculus, LISP, OCML, F# and even combinatorial logic but the main problem I have is how do you do things that require side effects like (interacting with a user, communicating with a remote service, or even handle simulating using random sampling) without violating the fundamental premise of pure functional programming which is, that for a given input the output is deterministic? I hope I am making sense, if not I welcome any attempts to properly educate me. Thanks in advance.

解决方案

Most real-world functional programming is not "pure" in most senses, so half of the answer to your question is "you do it by giving up on purity". That said, there are alternatives.

In the "purest" sense of pure, the entire program represents a single function of one or more arguments, returning a value. If you squint your eyes and wave your hands a bit, you can declare that all user input is part of the function's "arguments" and that all output is part of the "return value" and then fudge things a bit so that it only does the actual I/O "on demand".

A similar perspective is to declare that the input to the function is "the entire state of the outside world" and that evaluating the function returns a new, modified "state of the world". In that case, any function in the program that uses the world state is obviously freed from being "deterministic" since no two evaluations of the program will have exactly the same outside world.

If you wanted to write an interactive program in the pure lambda calculus (or something equivalent, such as the esoteric language Lazy K), that's conceptually how you'd do it.

In more practical terms, the problem comes down to making sure that I/O occurs in the correct order when input is being used as an argument to a function. The general structure of the "pure" solution to this problem is function composition. For instance, say you have three functions that do I/O and you want to call them in a certain order. If you do something like RunThreeFunctions(f1, f2, f3) there's nothing to determine the order they'll be evaluated in. On the other hand, if you let each function take another function as an argument, you can chain them like this: f1( f2( f3())), in which case you know that f3 will be evaluated first because the evaluation of f2 depends on its value. [Edit: See also the comment below about lazy vs. eager evaluation. This is important, because lazy evaluation is actually quite common in very pure contexts; e.g., the standard implementation of recursion in the pure lambda calculus is nonterminating under eager evaluation.]

Again, to write an interactive program in the lambda calculus, this is how you'd probably do it. If you wanted something actually usable for programming in, you'd probably want to combine the function composition part with the conceptual structure of functions taking and returning values representing the state of the world, and create some higher-order abstraction to handling pipelining the "world state" values between I/O functions, ideally also keeping the "world state" contained in order to enforce strict linearity--at which point you've all but reinvented Haskell's IO Monad.

Hopefully that didn't just make you even more confused.

这篇关于在纯函数式编程中可能会出现副作用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆