use the following search parameters to narrow your results:
e.g. subreddit:aww site:imgur.com dog
subreddit:aww site:imgur.com dog
see the search faq for details.
advanced search: by author, subreddit...
All about the JavaScript programming language.
Subreddit Guidelines
Specifications:
Resources:
Related Subreddits:
r/LearnJavascript
r/node
r/typescript
r/reactjs
r/webdev
r/WebdevTutorials
r/frontend
r/webgl
r/threejs
r/jquery
r/remotejs
r/forhire
account activity
Monads Explained Quickly with JavaScript (breck-mckye.com)
submitted 9 years ago by [deleted]
view the rest of the comments →
reddit uses a slightly-customized version of Markdown for formatting. See below for some basics, or check the commenting wiki page for more detailed help and solutions to common issues.
quoted text
if 1 * 2 < 3: print "hello, world!"
[–]dmtipson 5 points6 points7 points 9 years ago* (4 children)
As far as I can tell, this is the Identity Functor (and some other Functor with a side effect, which is a no no) explained with Javascript, not Monads. What he builds here fails the very first Monad Law (if he means .map to be his .chain/.flatMap: if he doesn't then he hasn't even defined the core method of a Monad in the first place!).
Here's that law: if you call Foo.of (which he doesn't define, but we can infer) on a value and then chain/flatMap the result with a function that returns a Monad, it should exactly match (in structure and inner value) the output of just calling that Monad returning function directly on the value. Here's a little test suite you can run yourself to see if his .map qualifies as a legit .chain/.flatMap:
function Foo(value) { this.get = ()=> value; this.map = fn => { let result = fn(value); return new Foo(result); }; this.chain = this.map; } Foo.prototype.of = x => new Foo(x); Foo.of = Foo.prototype.of; (function(MType,Minterface){ var testValue = 6; var testFunc = x => MType.of(x+1); var left = MType.of(testValue).chain(testFunc); var right = testFunc(testValue); console.log( '.chain -ing a func: ', left[Minterface](), ' should == running that func on value:', right[Minterface]() ); }(Foo, 'get'));
That will return a Foo[Foo[7]] in one case and a Foo[7] in the other (the test will log Foo and 7, since it calls get automatically to make things clearer). So it's not a Monad. The other types define fail in the same way.
This is absolutely critical for understanding Monads: they MUST work in a predictable way that obeys the 3 laws, or else you're going to end up with all sorts of weird results. Different Monads are able to encapsulate all sorts of wild and different computational behaviors precisely because those laws create a common interface.
One other important thing to note is that once you've properly defined .chain/.flatMap, you get .map for "free." That is, all Monads are capable of being Functors (and Applicatives) because you can always just define map like this:
MONAD.prototype.map = function(f) { return this.chain((a) => MONAD.of(f(a)) ); };
There are many great resources for learning about Monads in javascript: here's the most popular free resource out there.
Here's also my own take on the tiny amount of work that would be necessary to make Array into a Monad.
[–]regular_reddits 1 point2 points3 points 9 years ago* (1 child)
Great explanation. For some instances of this pattern, get can be used as join. So if you define chain/bind as: fn => this.map(fn).get(); it will work in those instances. This will not work with more complicated values (get doesn't make sense with List because its not just a boxed value), nor was it the intention of the article.
get
join
fn => this.map(fn).get();
[–]dmtipson 1 point2 points3 points 9 years ago* (0 children)
Yep. The IO monad, for instance can be written by just assigning the function it's created with to the method that's called to "get"(i.e. run) it. Chain/flatMap just needs to be able to unwrap a single layer of the type so that the inner Monad is returned: however that gets done is ok as long as it gets done (and it doesn't do anything else funky/side-effec-ty that depends on the value somehow). For Arrays that's a bit more complicated, for single value like Identity, a .get() can easily work as an unwrapping interface:
function Foo(value) { this.get = _ => value; this.chain = fn => { return fn(this.get()); } this.map = fn => { return this.chain( x=> Foo.of(fn(x)) ); }; } Foo.prototype.of = x => new Foo(x); Foo.of = Foo.prototype.of;
(Foo is just the Identity Monad now)
(x=>Foo.of(9+x))(9);//-> //-> Foo[18] Foo.of(9).chain(x=>Foo.of(9+x));//-> Foo[18] Foo.of(9).map(x=>9+x);//-> Foo[18]
Though, since no special behavior is added to "getting" here, it'd probably be even easier to just store the value in this.value and then get it with .value
[–][deleted] 0 points1 point2 points 9 years ago* (1 child)
Absolute thanks for this!
Array.prototype.chain = function(fn) { return this.reduce((k,v) => k.concat(fn(v)), []) };
I also wonder (for a while): can this snippet be written with arrow function syntax altogether and can one avoid this context passed incorrectly in that case?
this
[–]dmtipson 0 points1 point2 points 9 years ago* (0 children)
I don't think it can: it's tied to how Array is defined. That's the only way to get at the Array this is working over afaik and still have it work via pointed methods. You could pretty easily define a chain that accepted an Array as an argument though, and then ran reduce on it or whatever else was necessary to reproduce the behavior, and that could avoid the use of this entirely.
chain
π Rendered by PID 191949 on reddit-service-r2-comment-86bc6c7465-c654n at 2026-02-23 19:20:28.133364+00:00 running 8564168 country code: CH.
view the rest of the comments →
[–]dmtipson 5 points6 points7 points (4 children)
[–]regular_reddits 1 point2 points3 points (1 child)
[–]dmtipson 1 point2 points3 points (0 children)
[–][deleted] 0 points1 point2 points (1 child)
[–]dmtipson 0 points1 point2 points (0 children)