【问题标题】:Is this a monad?这是单子吗?
【发布时间】:2011-06-30 19:07:20
【问题描述】:

我正在尝试理解 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 上下文始终是那个 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 是单子吗?

EDIT修改后的代码在github上https://github.com/jcubic/monadic

【问题讨论】:

  • 你不应该向我们展示函数“M”本身吗?
  • 据我所知,JavaScript 没有 Monads 作为语言的原生部分。您可能能够模仿它们,但没有像 Haskell 或 LISP 中的 monad 那样“真实”。所以从技术上讲不是。
  • Haskell 在添加单子语法之前已经存在多年。你真正需要的只是高阶函数。
  • 此代码 - jcubic.pl/monad.js
  • 我读到 jQuery 是 Monad importantshock.wordpress.com/2009/01/18/jquery-is-a-monad 并且在维基百科中有“Monads 允许程序员将动作链接在一起以构建管道,其中每个动作都装饰有由单子。”

标签: javascript functional-programming monads


【解决方案1】:

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

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

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

【讨论】:

    【解决方案2】:

    除了可变性;据我了解,您所写的内容更接近于应用函子,而不是 monad 或 monoid。

    再一次,据我的理解,幺半群是一个在将单个类型映射到自身的 single 操作下封闭的组(在抽象代数意义上)。如果您实现了add,那么您可能您的原型链实现了一个幺半群。但即便如此,您也必须自己手动指定归约,作为一个二元运算,在每个参数之间,如下所示:

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

    但是由于您已将不确定数量的函数绑定到一个类型 (M),它们都知道如何“解包”该类型,应用预期的函数,然后在退出时恢复“包装器”,那么您就有了一种应用函子。

    如果您找到了一些方法来组合在M 上运行的所有函数的作用域,那么您将更接近一元实现:

    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
    

    这样的实现很棘手,但让您能够将它们切成小块,和/或将较小的部分组合成较大的部分。

    【讨论】:

    • @jcubic 您的编辑实际上降低了可读性,请撤消它们。
    猜你喜欢
    • 1970-01-01
    • 2013-11-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-28
    • 1970-01-01
    相关资源
    最近更新 更多