Functional programming 纯函数式编程是否可能产生副作用

Functional programming 纯函数式编程是否可能产生副作用,functional-programming,side-effects,Functional Programming,Side Effects,我已经试着对函数式编程有一段时间了?我查阅了lambda演算、LISP、OCML、F#甚至组合逻辑,但我遇到的主要问题是如何处理需要副作用的事情,例如(与用户交互、与远程服务通信,甚至使用随机采样处理模拟)在不违背纯函数式编程的基本前提下,即对于给定的输入,输出是确定性的?我希望我说的有道理,如果不是,我欢迎任何适当教育我的尝试。提前感谢。函数式编程是关于限制和隔离副作用,而不是试图完全消除它们。。。因为你不能 。。。是的,我发现FP很有用(当然是在Erlang中):我发现从“想法”到“计划”(

我已经试着对函数式编程有一段时间了?我查阅了lambda演算、LISP、OCML、F#甚至组合逻辑,但我遇到的主要问题是如何处理需要副作用的事情,例如(与用户交互、与远程服务通信,甚至使用随机采样处理模拟)在不违背纯函数式编程的基本前提下,即对于给定的输入,输出是确定性的?我希望我说的有道理,如果不是,我欢迎任何适当教育我的尝试。提前感谢。

函数式编程是关于限制和隔离副作用,而不是试图完全消除它们。。。因为你不能


。。。是的,我发现FP很有用(当然是在Erlang中):我发现从“想法”到“计划”(或问题到解决方案;)更容易。。。当然,那可能就是我。

你至少需要知道另一个基本概念:。你需要这个来做I/O和其他“有用”的东西

即使你不在工作中使用它,学习一种或多种函数式编程语言也是学习以不同的方式思考的好方法,并为你提供了一套解决问题的替代方法(当你不能像其他语言中的函数式方法那样干净利落时,它也会让你感到沮丧)


它使我更擅长编写XSL样式表。

Haskell是一种纯函数式编程语言。在Haskell中,所有函数都是纯函数(即,它们总是为相同的输入提供相同的输出)。但是你如何处理哈斯克尔的副作用呢?好吧,通过使用,这个问题很好地解决了

以I/O为例。在Haskell中,每个执行I/O的函数都返回一个IO计算,即IO单子中的计算。例如,一个函数从键盘读取一个int,而不是返回一个int,它返回一个IO计算,在运行时产生一个int:

askForInt :: String -> IO Int
例如,由于它返回的是I/O计算,而不是
Int
,因此不能将此结果直接用于求和。为了访问
Int
值,您需要“展开”计算。唯一的方法是使用bind函数(
>=
):


因为这也会返回一个IO计算,所以最终总是会得到一个I/O计算。这就是Haskell隔离副作用的方法。IO monad作为真实世界状态的抽象(事实上,在封面下,它通常使用状态部分名为
RealWorld
的类型来实现)。

Haskell的方法是使用monad,请参见和Haskell对其的解释

基本上,这个想法是你不能摆脱IO单子。我的理解是,您能够链接打开IO单子并执行该函数的函数。但您无法完全删除IO单子

另一个使用与IO没有直接关联的Monad的例子是Maybe Monad。与IO单子相反,此单子是“不可破解的”。但是用Maybe monad解释monad的用法更容易。让我们假设您具有以下函数

wrap :: Maybe x -> (x -> y) -> Maybe y
wrap Nothing  f = Nothing
wrap (Just x) f = Just (f x)
现在您可以调用
wrap(Just 4)(5+)
,它将返回
Just 9


IO monad的思想是可以在内部类型上使用(+5)之类的函数。monad将确保函数以串行方式调用,因为每个函数都与包装IO monad链接

与用户交互和与远程服务通信确实需要软件中某种非功能部分

许多“函数式语言”(像大多数Lisp一样)不是纯粹的函数式语言。他们仍然允许你做一些有副作用的事情,尽管在大多数情况下副作用的事情是“不被鼓励的”

Haskell是“纯功能性”的,但仍然允许您通过IO monad执行非功能性的操作。其基本思想是,您的纯功能程序发出一个惰性数据结构,该结构由一个非功能程序(您不编写它,它是环境的一部分)计算。有人可能会说,这种数据结构本身就是一个必要的程序。所以你在用函数式语言进行命令式元编程


忽略哪种方法“更好”,这两种情况下的目标都是在程序的功能部分和非功能部分之间建立分离,并尽可能限制非功能部分的大小。功能部分往往更易于重用、测试和推理。

大多数现实世界的功能编程在大多数意义上都不是“纯”的,所以你问题的一半答案是“你通过放弃纯”来实现它。也就是说,还有其他选择

在pure的“最纯粹”意义上,整个程序代表一个或多个参数的单个函数,返回一个值。如果您眯起眼睛,挥动双手,可以声明所有用户输入都是函数“参数”的一部分,所有输出都是“返回值”的一部分,然后稍加捏造,使其仅“按需”执行实际I/O

类似的观点是,声明函数的输入是“外部世界的整个状态”,对函数求值将返回一个新的、修改过的“世界状态”。在这种情况下,程序中使用世界状态的任何函数显然都不具有“确定性”,因为程序的两个求值都不会具有完全相同的外部世界

如果您想用纯lambda演算(或类似的语言,如深奥的语言Lazy K)编写一个交互式程序,从概念上讲,您应该这样做

在更实际的情况下,问题归结为当输入被用作函数的参数时,确保I/O以正确的顺序发生。这个问题的“纯”解的一般结构i
wrap :: Maybe x -> (x -> y) -> Maybe y
wrap Nothing  f = Nothing
wrap (Just x) f = Just (f x)