Functional programming 国家单子的目的是什么?

Functional programming 国家单子的目的是什么?,functional-programming,monads,state-monad,Functional Programming,Monads,State Monad,我是一名JavaScript开发人员,正在努力提高我的函数式编程技能。我最近在管理国家方面遇到了困难。在寻找解决方案时,我在各种文章和视频中都提到了国家单子,但我真的很难理解它。我想知道这是否是因为我期望它是它不是的东西 我试图解决的问题 在web客户端中,我从后端获取资源。为了避免不必要的流量,我在客户端创建了一个简单的缓存,其中包含已经获取的数据。缓存是我的状态。我希望我的几个模块能够保存对缓存的引用,并查询其当前状态,该状态可能已被另一个模块修改 这在javascript中当然不是问题,因

我是一名JavaScript开发人员,正在努力提高我的函数式编程技能。我最近在管理国家方面遇到了困难。在寻找解决方案时,我在各种文章和视频中都提到了国家单子,但我真的很难理解它。我想知道这是否是因为我期望它是它不是的东西

我试图解决的问题 在web客户端中,我从后端获取资源。为了避免不必要的流量,我在客户端创建了一个简单的缓存,其中包含已经获取的数据。缓存是我的状态。我希望我的几个模块能够保存对缓存的引用,并查询其当前状态,该状态可能已被另一个模块修改

这在javascript中当然不是问题,因为它可以改变状态,但我想了解更多关于函数式编程的知识,我希望状态单子能帮助我

我所期望的 我假设我可以做这样的事情:

var state = State.of(1);
map(add(1), state);
state.evalState() // => 2 
这显然行不通。状态总是1

我的问题 我对单子状态的假设是错误的,还是我只是用错了

我意识到我可以做到这一点:

var state = State.of(1);
var newState = map(add(1), state);

。。。而
newState
的状态将为2。但在这里,我并没有真正看到state monad的用法,因为我必须创建一个新实例才能更改值。在我看来,这似乎是函数式编程中经常做的事情,在函数式编程中,值是不可变的。

它确实像您的第二个描述一样,返回一个新的不可变状态。然而,如果你这样称呼它,它并不是特别有用。如果你有一大堆你想调用的函数,每一个函数都会获取上一步返回的状态,然后返回一个新的状态,可能还有另一个值

使其成为monad基本上允许您只指定要执行的函数名的列表,而不是重复
newState=f(initialState);newState=g(newState);最终状态=h(新状态)一遍又一遍。Haskell有一个名为do notation的内置表示法来精确实现这一点。如何在JavaScript中实现它取决于您使用的函数库,但在最简单的形式(没有任何中间结果绑定)中,它可能看起来像
finalState=do([f,g,h],initialState)


换句话说,状态单子不会神奇地使不变性看起来像易变性,但它可以简化在某些情况下对中间状态的跟踪。

状态单子的目的是隐藏函数之间的状态传递

让我们举一个例子:

方法A和B需要使用某种状态并对其进行变异,而B需要使用A变异的状态。在具有不可变数据的函数式语言中,这是不可能的

而是这样做的:初始状态与所需的参数一起传递给A,A返回结果和“修改”状态——实际上是一个新值,因为原始状态没有更改。这个“新”状态(也可能是结果)连同它所需的参数一起传递给B,B返回它的结果和它(可能)修改过的状态

显式地传递这个状态是一个PITA,因此状态monad将其隐藏在它的monadic封面下,允许需要访问状态的方法通过
get
set
monadic方法来访问它

为了使用有状态计算A和B,我们将它们组合到一个聚合有状态计算中,并给该聚合一个开始状态(和参数)来运行,它返回一个最终的“修改”状态和结果(在通过A、B和它所包含的任何其他内容运行之后)

在我看来,从您所描述的内容来看,您似乎在寻找更类似于的东西,其中状态在参与者中进行管理,其余的代码通过它与之交互,检索(不可变的版本)它或通过消息告诉它要修改。在不可变语言(如Erlang)中,参与者阻塞等待消息,然后在消息传入时处理消息,然后通过(尾部)递归循环;它们将任何修改的状态传递给递归调用,这就是状态如何被“修改”的


不过,正如您所说,因为您使用的是JavaScript,所以这不是什么大问题。

我试图从JavaScript开发人员的角度回答您的问题,因为我相信这是您的问题的原因。也许您可以在标题和标记中指定术语Javascript

将概念从Haskell转移到Javascript基本上是一件好事,因为Haskell是一种非常成熟的纯函数式语言。然而,它可能会导致混淆,就像状态单子一样

例如,maybe monad很容易理解,因为它处理两种语言都面临的一个问题:不返回值(Javascript中的
null
/
undefined
)可能导致计算出错
Maybe
可以避免开发人员在整个代码中分散
null
检查

在国家单子的情况下,情况有点不同。在Haskell中,状态单子是组成函数所必需的,这些函数共享可变状态,而不必传递这种状态。State是一个或多个不在所涉及函数的参数中的变量。在Javascript中,您只需执行以下操作:

var堆栈={
门店:[],
push:函数push(element){this.store.push(element);返回this;},
函数pop(){返回this.store.pop();}
}
console.log(stack.push(1.push(2.push(3.pop());//3(有状态计算的返回值)
console.log(stack.store);//[1,2](突变的全局状态)
这是所需的有状态计算和
s