在纯函数式编程中是否可能产生副作用 [英] Are side-effects possible in pure functional programming

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

问题描述

我一直试图围绕函数式编程进行一段时间.我查阅了 lambda 演算、LISP、OCaml、F# 甚至组合逻辑,但我遇到的主要问题是 - 你如何做需要副作用的事情,例如:

I have been trying to wrap my head around functional programming for a while now. I have looked up lambda calculus, LISP, OCaml, F# and even combinatorial logic but the main problem I have is this - how do you do things that require side effects like:

  • 与用户互动,
  • 与远程服务通信,或
  • 使用随机抽样处理模拟

不违反纯函数式编程的基本前提,即对于给定的输入,输出是确定性的?

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 help me understand. 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.

在最纯粹"的纯粹意义上,整个程序表示一个或多个参数的单个函数,返回一个值.如果你眯着眼睛稍微挥动一下手,你可以声明所有用户输入都是函数参数"的一部分,所有输出都是返回值"的一部分,然后稍微捏造一下,让它只做按需"的实际 I/O.

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.

如果您想用纯 lambda 演算(或类似的东西,例如深奥的语言 Lazy K)编写交互式程序,从概念上讲,您可以这样做.

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.

从更实际的角度来看,问题归结为确保在将输入用作函数的参数时 I/O 以正确的顺序发生.这个问题的纯"解决方案的一般结构是函数组合.例如,假设您有三个执行 I/O 的函数,并且您想按特定顺序调用它们.如果您执行类似 RunThreeFunctions(f1, f2, f3) 的操作,则无法确定它们的计算顺序.另一方面,如果您让每个函数都将另一个函数作为参数,你可以像这样链接它们:f1( f2( f3())),在这种情况下你知道 f3 将首先被评估,因为 f2 的评估 取决于它的值.[另请参阅下面关于懒惰与急切评估的评论.这很重要,因为惰性求值实际上在非常纯的上下文中很常见;例如,纯 lambda 演算中递归的标准实现在急切求值下是非终止的.]

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. [ 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.]

同样,要在 lambda 演算中编写交互式程序,您可能会这样做.如果您想要一些实际可用于编程的东西,您可能希望将函数组合部分与函数的概念结构结合起来,这些函数接受和返回表示世界状态的值,并创建一些更高阶的抽象来处理世界状态"值在 I/O 函数之间,理想情况下还包含世界状态"以强制执行严格的线性——在这一点上,您几乎重新发明了 Haskell 的 IO Monad.

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天全站免登陆