Skip to content

Instantly share code, notes, and snippets.

@elclanrs
Last active June 23, 2021 15:12
Show Gist options
  • Save elclanrs/8090698 to your computer and use it in GitHub Desktop.
Save elclanrs/8090698 to your computer and use it in GitHub Desktop.
Monads in JavaScript
var extend = function(a, b) {
for (var i in b)
a[i] = b[i];
return a;
};
var fluent = function(f) {
return function() {
var clone = extend(Object.create(null), this);
f.apply(clone, arguments);
return clone;
};
};
var toArray = function(x) {
return Array.prototype.slice.call(x);
};
var Either = {
unit: function(x) {
return this.right(x);
},
bind: function(f) {
if (this.isLeft) {
return this.left(this.l);
} else if (this.isRight) {
return f.call(this, this.r);
}
},
left: fluent(function(l) {
this.l = l;
this.isLeft = true;
this.toString = function() {
return "Left: " + JSON.stringify(this.l);
};
this.valueOf = function() {
return this.l;
};
}),
right: fluent(function(r) {
this.r = r;
this.isRight = true;
this.toString = function() {
return "Right: " + JSON.stringify(this.r);
};
this.valueOf = function() {
return this.r;
};
})
};
var div = function(x, y) {
if (y === 0) {
return Either.left('division by 0');
}
return Either.right(x/y);
};
var result = div(4,2).bind(function(x) {
return div(x,2).bind(function(y) {
return this.unit(x + y);
});
});
console.log(result.toString()); //=> Right: 3
var result = div(4,2).bind(function(x) {
return div(x,0).bind(function(y) {
return this.unit(x + y);
});
});
console.log(result.toString()); //=> Left: division by 0
var List = {
unit: fluent(function() {
this.x = toArray(arguments);
}),
bind: function(f) {
var fx = this.x.map(f.bind(this));
var a = fx[0];
for (var i=1; i<fx.length; i++)
a.x = a.x.concat(fx[i].x);
return a;
},
lift: function(f) {
return function(x) {
return List.unit(f(x));
}
},
toString: function() {
return 'List: '+ JSON.stringify(this.x);
},
valueOf: function() {
return this.x;
}
};
var add1 = function(x) {
return x + 1;
};
// lift
var result = List.unit(1,2,3).bind(List.lift(add1));
console.log(result.toString()); //=> List: [2,3,4]
// do
var result = List.unit(1,2).bind(function(x) {
return this.unit(3,4).bind(function(y) {
return this.unit(x + y);
});
});
console.log(result.toString()); //=> List: [4,5,5,6]
var Maybe = {
unit: function(x) {
return this.just(x);
},
bind: function(f) {
if (this.isNothing) {
return this.nothing();
} else if (this.isJust) {
return f.call(this, this.x);
}
},
lift: function(f) {
var self = this;
return function() {
if (~toArray(arguments).indexOf(null)) {
return self.nothing();
}
return self.just(f.apply(self, arguments));
};
},
nothing: fluent(function() {
this.x = null;
this.isNothing = true;
this.toString = function() {
return 'Nothing';
};
}),
just: fluent(function(x) {
this.x = x;
this.isJust = true;
this.toString = function() {
return 'Just: '+ JSON.stringify(this.x);
};
}),
valueOf: function() {
return this.x;
}
};
var by2 = function(x) {
return x * 2;
};
var add = function(x, y) {
return x + y;
};
var result = Maybe.unit(2).bind(Maybe.lift(by2));
console.log(result.toString()); //=> Just: 4
var addM = Maybe.lift(add);
var result = addM(1,2).bind(function(x) {
return addM(x,3).bind(function(y) {
return this.unit(x + y);
});
});
console.log(result.toString()); //=> Just: 9
var result = addM(1,2).bind(function(x) {
return addM(x,null).bind(function(y) {
return this.unit(x + y);
});
});
console.log(result.toString()); //=> Nothing
var Writer = {
unit: fluent(function(x, w) {
this.w = w == null ? [] : [w];
this.x = x;
}),
bind: function(f) {
var w = this.w
, fx = f.call(this, this.x);
fx.w = w.concat(fx.w);
return fx;
},
tell: fluent(function(w) {
this.w = [w];
}),
toString: function() {
return 'Writer: ('+ this.x +', '+ JSON.stringify(this.w) +')';
},
valueOf: function() {
return this.x;
}
};
var add = function(x, y) {
return Writer.unit(x + y, 'add '+ x +' plus '+ y);
};
var result = add(1,2).bind(function(x) {
return add(x,2).bind(function(y) {
return this.tell('multiply '+ x +' by '+ y).bind(function() {
return this.unit(x * y);
});
});
});
console.log(result.toString());
//^ Writer (15, ["add 1 plus 2", "add 3 plus 2", "multiply 3 by 5"])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment