这是一个单子吗?

时间:2011-02-20 15:56:46

标签: javascript functional-programming monads

我正在尝试理解monads的概念,我想知道这段代码是否是这个概念的实现(在JavaScript中)。

我有函数M返回具有set方法的新对象,该方法创建包装器方法

var foo = M().set('getX', function() { 
  return this.x; 
}).set('setX', function(x) { 
  this.x = x;
}).set('addX', function(x) { 
  this.x += x;
});

然后我可以链接foo的方法

foo.setX(10).addX(20).addX(30).getX()

将返回60

,如果我有方法对象并使用此对象调用M,则相同。

var foo = {
  x: 10,
  add: function(x) {
    this.x += x;
  }
};

M(foo).add(10).add(20).add(30).x

将返回70

函数包含在M对象中,因此方法中的this context始终是M对象。

f = M({x: 20}).set('getX', function() {
   return this.x; 
}).set('addX', function(x) {
   this.x += x;
}).addX(10).getX

所以f是由M包裹的对象的上下文的函数 - 如果我调用f()它将返回30。

我理解正确吗? M是monad吗?

编辑修改后的代码位于github https://github.com/jcubic/monadic

2 个答案:

答案 0 :(得分:14)

这是一种幺半群模式。每个状态更新操作,例如.setX(10).addX(20)等,都是转换一个对象的计算。 (要在语法上有效,你必须把它写成一个参数函数function(x) {x.addX(20);},但我认为如果我使用短格式会更清楚。)

两件事使这成为一个幺半群。首先,有一个标识元素:.addX(0)对其对象没有任何作用。其次,可以组合任何两个操作。例如,.setX(10).addX(20)也是一个转换一个对象的计算。

这不是一个单子。您的方法支持的计算仅限于编写和更新this.x。 (.getX()不是幺半群的成员,因为你不能在它之后链接任何东西)。例如,使用monad,您可以让一个操作链中的一个成员执行if-then-else来决定链中接下来会发生什么。你的方法不能那样做。

答案 1 :(得分:0)

除了可变性;根据我的理解,你所写的内容比一个monad或monoid更接近一个applicative functor。

同样,根据我的理解,monoid是一个组(在抽象代数意义上)在操作下关闭,将单个类型映射到自身。如果你只有 实现了add,那么你可能你的原型链实现了一个monoid。但即使这样,你也必须亲自指定减少,作为二元操作,在每个参数和每个参数之间,如下所示:

M({x:0}).add(1).add(2)...add(100) === 1050; // or _.reduce([1..100],add)

但是因为你已经将一个不确定数量的函数绑定到一个类型(M),它们都知道如何解开'该类型,应用预期的功能,然后恢复'包装'在退出时,你有一种应用函子。

如果您已经找到某种方法来编写在M上运行的所有函数的范围,那么您将更接近于monadic实现:

var bigOpFromLittleOps = 
       M({x:0})  .bind(function(x0){
return Madd(1)   .bind(function(x1){
return Madd(2)   .bind(function(x2){
...
return Madd(100) .bind(function(x100){
return Mreturn(x100);
}); ... });});})() === 1050; // Overkill

这样的实现很棘手,但是你可以将它们切片并分成小片,和/或从较小的片段中构成较大的片段。