最近我读了很多关于函数式编程的东西,大部分我都能理解,但有一件事我就是搞不懂,那就是无状态编码。在我看来,通过删除可变状态来简化编程就像通过删除仪表盘来“简化”一辆汽车:最终产品可能更简单,但希望它能与最终用户交互。

几乎我能想到的每个用户应用程序都将状态作为核心概念。如果你写了一个文档(或一个SO post),状态会随着每一个新的输入而改变。或者如果你玩电子游戏,会有大量的状态变量,从所有角色的位置开始,这些角色往往会不断移动。如果不跟踪不断变化的值,您怎么可能做任何有用的事情呢?

每次我发现一些讨论这个问题的东西,它都是用真正的技术函数语言写的,假设我没有浓厚的FP背景。有谁知道如何向那些对命令式编码有很好的、扎实的理解,但在函数方面完全是n00b的人解释这一点吗?

编辑:到目前为止,一堆回复似乎试图让我相信不可变值的优点。我懂你的意思。这很有道理。我不明白的是,在没有可变变量的情况下,如何跟踪必须不断变化的值。


当前回答

通过使用大量的递归。

用f#(一种函数式语言)玩地字游戏。

其他回答

除了别人给出的很好的答案,想想Java中的Integer和String类。这些类的实例是不可变的,但这并不意味着仅仅因为它们的实例不可更改,这些类就毫无用处。不可变性给了你一定的安全性。您知道,如果使用String或Integer实例作为Map的键,则该键不能更改。将其与Java中的Date类进行比较:

Date date = new Date();
mymap.put(date, date.toString());
// Some time later:
date.setTime(new Date().getTime());

你已经无声地改变了地图中的一个键!使用不可变对象(如在函数式编程中)要干净得多。更容易推断会发生什么副作用——没有!这意味着程序员更容易,优化器也更容易。

只是做同一件事的不同方式。

Consider a simple example such as adding the numbers 3, 5, and 10. Imagine thinking about doing that by first changing the value of 3 by adding 5 to it, then adding 10 to that "3", then outputting the current value of "3" (18). This seems patently ridiculous, but it is in essence the way that state-based imperative programming is often done. Indeed, you can have many different "3"s that have the value 3, yet are different. All of this seems odd, because we have been so ingrained with the, quite enormously sensible, idea that the numbers are immutable.

现在,当您将值设置为不可变时,考虑添加3、5和10。3加5得到另一个值8,然后再加10得到另一个值18。

这是做同样事情的等价方法。所有必要的信息都存在于这两种方法中,只是形式不同。其中,信息以状态的形式存在,并存在于改变状态的规则中。在另一种情况下,信息存在于不可变数据和函数定义中。

你不可能有一种有用的纯函数式语言。总会有一定程度的可变性需要处理,IO就是一个例子。

将函数式语言视为您使用的另一种工具。它对某些事情有好处,但对其他事情没有好处。你给出的游戏例子可能不是使用函数语言的最佳方式,至少屏幕会有一个可变的状态,你不能用FP做任何事情。使用FP思考问题的方式和解决问题的类型将不同于使用命令式编程所习惯的方式。

JavaScript provides very clear examples of the different ways of approaching mutable or immutable state\values within its core because the ECMAScript specifications were not able to settle on a universal standard so one must continue to memorize or doublecheck which functions create a new object that they return or modify the original object passed to it. If your entire language is immutable then you know you are always getting a new (copied & possibly modified) result and never have to worry about accidentally modifying the variable before passing it into a function.

你知道哪个会返回一个新的对象,哪个会改变下面例子中的原始对象吗?

Array.prototype.push()
String.prototype.slice()
Array.prototype.splice()
String.prototype.trim()

请记住:函数式语言是图灵完备的。因此,任何用命令式语言执行的有用任务都可以用函数式语言完成。最后,我认为混合方法是有意义的。像f#和Clojure(我确信还有其他语言)这样的语言鼓励无状态设计,但在必要时允许可变性。