我试图从 Javascript 开发人员的角度回答您的问题,因为我相信这是您的问题的原因。也许您可以在标题和标签中指定术语 Javascript。
将概念从 Haskell 转移到 Javascript 基本上是一件好事,因为 Haskell 是一种非常成熟的纯函数式语言。但是,它可能会导致混乱,就像 state monad 一样。
例如,maybe monad 很容易理解,因为它处理两种语言都面临的一个问题:不返回值可能会出错的计算(JavaScript 中的null/undefined)。 Maybe 使开发人员免于在整个代码中分散null 检查。
在 state monad 的情况下,情况有点不同。在 Haskell 中,需要 state monad 来组合函数,这些函数共享可变状态,而无需传递此状态。状态是不属于所涉及函数的参数的一个或多个变量。在 Javascript 中,您可以执行以下操作:
var stack = {
store: [],
push: function push(element) { this.store.push(element); return this; },
pop: function pop() { return this.store.pop(); }
}
console.log(stack.push(1).push(2).push(3).pop()); // 3 (return value of stateful computation)
console.log(stack.store); // [1, 2] (mutated, global state)
这是所需的有状态计算,store 不必在方法之间传递。乍一看,没有理由在 Javascript 中使用 state monad。但由于 store 是公开可访问的,push 和 pop 会改变全局状态。改变全局状态是个坏主意。这个问题可以通过多种方式解决,其中之一就是 state monad。
以下简化示例将堆栈实现为状态单子:
function chain(mv, mf) {
return function (state) {
var r = mv(state);
return mf(r.value)(r.state);
};
}
function of(x) {
return function (state) {
return {value: x, state: state};
};
}
function push(element) {
return function (stack) {
return of(null)(stack.concat([element]));
};
}
function pop() {
return function (stack) {
return of(stack[stack.length - 1])(stack.slice(0, -1));
};
}
function runStack(seq, stack) { return seq(stack); }
function evalStack(seq, stack) { return seq(stack).value; }
function execStack(seq, stack) { return seq(stack).state; }
function add(x, y) { return x + y; }
// stateful computation is not completely evaluated (lazy evaluation)
// no state variables are passed around
var computation = chain(pop(), function (x) {
if (x < 4) {
return chain(push(4), function () {
return chain(push(5), function () {
return chain(pop(), function (y) {
return of(add(x, y));
});
});
});
} else {
return chain(pop(), function (y) {
return of(add(x, y));
});
}
});
var stack1 = [1, 2, 3],
stack2 = [1, 4, 5];
console.log(runStack(computation, stack1)); // Object {value: 8, state: Array[3]}
console.log(runStack(computation, stack2)); // Object {value: 9, state: Array[1]}
// the return values of the stateful computations
console.log(evalStack(computation, stack1)); // 8
console.log(evalStack(computation, stack2)); // 9
// the shared state within the computation has changed
console.log(execStack(computation, stack1)); // [1, 2, 4]
console.log(execStack(computation, stack2)); // [1]
// no globale state has changed
cosole.log(stack1); // [1, 2, 3]
cosole.log(stack2); // [1, 4, 5]
可以避免嵌套函数调用。为简单起见,我省略了此功能。
在 Javascript 中没有任何问题可以单独使用 state monad 来解决。并且很难理解像 state monad 这样概括的东西,它解决了所用语言中看似不存在的问题。它的使用只是个人喜好问题。