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
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.
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.