13

I'm trying to understand the concept of monads and I want to know if this code is an implementation of this concept (in JavaScript).

I have function M which return new object that have set method which create wrapper method

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

And then I can chain method of foo

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

will return 60

and the same if I have object with methods and call M with this object.

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

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

will return 70

Functions are wrapped inside M object so the this context inside method is always that M object.

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

so f is function with context of object wrapped by M — if I call f() it will return 30.

Am I understand this correctly? Is M a monad?

EDIT modified code is on github https://github.com/jcubic/monadic

jcubic
  • 61,973
  • 54
  • 229
  • 402
  • Shouldn't you show us the function "M" itself? – Pointy Feb 20 '11 at 16:04
  • As far as I'm aware JavaScript does not have Monads as a native part of the language. You may be able to emulate them but there not as "real" as the monads in say Haskell or LISP. So technically no it is not. – Raynos Feb 20 '11 at 16:05
  • 14
    Haskell existed for years before monadic syntax was added. All you really need are higher order functions. – sclv Feb 20 '11 at 16:09
  • code for this - http://jcubic.pl/monad.js – jcubic Feb 20 '11 at 16:11
  • I read that jQuery is a Monad http://importantshock.wordpress.com/2009/01/18/jquery-is-a-monad/ and in wikiedia there is that "Monads allow the programmer to chain actions together to build a pipeline, in which each action is decorated with additional processing rules provided by the monad." – jcubic Feb 20 '11 at 16:14
  • Some javascript does this natively: "This is a string".split(" ").join("/").toUpperCase().bold() – mplungjan Feb 20 '11 at 16:23
  • @mplungjan this is method chaining, but there is no specific monad type object. – jcubic Feb 20 '11 at 16:35

2 Answers2

14

This is a monoid pattern. Each state-updating operation, such as .setX(10), .addX(20), and so forth, is a computation that transforms one object. (To be syntactically valid, you would have to write it as a one-parameter function function(x) {x.addX(20);}, but I think it's clearer if I use the short form.)

Two things make this a monoid. First, there is an identity element: .addX(0) does nothing to its object. Second, any two operations can be combined. For example, .setX(10).addX(20) is also a computation that transforms one object.

It is not a monad. The computations supported by your methods are limited to writing and updating this.x. (.getX() is not a member of the monoid because you can't chain anything after it). For example, with a monad you can have one member of a chain of operations execute an if-then-else to decide what comes next in the chain. Your methods can't do that.

Heatsink
  • 7,721
  • 1
  • 25
  • 36
0

Mutability aside; to my understanding, what you have written is closer to an applicative functor than either a monad, or a monoid.

Again, to my understanding, a monoid is a Group (in the abstract algebraic sense) closed under a single operation mapping a single type unto itself. If you had only implemented add then you might say that your prototype chain implemented a monoid. But even then, you would have to specify the reduction yourself, by hand, as a binary operation, between each, and every argument, like so:

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

But since you have bound an indeterminate number of functions to a type (M), which all know how to 'unwrap' that type, apply the intended function, then restore the 'wrapper' on exit, then you have a sort of applicative functor.

If you had found some way to compose the scopes of all functions operating on M, then you would be closer still to a monadic implementation:

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

Such implementations are tricky, but give you the ability to slice and dice them into little pieces, and/or compose larger ones from smaller ones.

jcubic
  • 61,973
  • 54
  • 229
  • 402
theoski
  • 109
  • 1
  • 5