The article doesn't mention lawfulness until the very end. But lawfulness is a crucial part of the definition and usage.
Because, a lot of the convenience and ergonomics that we can derive using these abstractions comes from being able to rely on the laws. Without them, they are just not really useful.
And when looking at the examples, I can totally see that someone reads
> That's all Monad is. It is an implementation of some "method" that conforms to that interface specification.
and then makes "Set" or "HashMap" implement the interface unlawful and bad things happen when they are least expected.
In my opinion, in a programming context, that's Haskell specific. Most programmers in other languages won't worry about the laws. The laws are less useful and less relevant in strict languages, as is the case with many laws, which is why Haskell programmers are just about the only ones discussing them.
I label this my opinion quite on purpose, because I see the alternative viewpoint. I think you'll be more successful taking about lawfulness once someone gets the mechanics, given how much that has been a stopper over the years!
Don't get me wrong - I'm not saying the article is bad or not useful.
However, Monad and Functor are well defined terms. If you write an article where you complain about confusing explanations and try to do better, then you must start with a disclaimer: "I'm not explaining Monads as they are defined in math. I'm explaining something that is quite similar to it to make it more practical and later explain the actual term" or so.
Otherwise you are just adding to the confusion that people experience.
> The laws are less useful and less relevant in strict languages, as is the case with many laws, which is why Haskell programmers are just about the only ones discussing them.
I'm not sure if I would agree with that. But even if I would, the laws are still really important, even in strict languages. I've implemented quite a few monad instances in Scala (which is a strict language) and I'm speaking from experience.
I don't know why just saying the following block of text combined with walking through a bunch of examples and how each of the examples fulfills those properties, wouldn't be sufficient :
'
If F is a functor,
then for any A,B,C,f,g, such that f : A -> B , and g : B -> C,
(where we write composition with a semicolon so that the composition of f and g is written f;g : A -> C),
F(f) : F(A) -> F(B)
and F(g) : F(B) -> F(C)
and F(f;g) : F(A) -> F(C)
and F(f;g) = F(f) ; F(g)
(also, if id_A : A -> A is the identity on A, the F(id_A) : F(A) -> F(A) is the identity on F(A) )
'
(in case they are unfamiliar with the notation f : A -> B , then this can be explained in each of the first few examples.)
I guess it might also be good to give some counterexamples, and point out how the counterexamples fail to be functors, and why they are also not nice.
Implementing "unlawful monads" would be equal to giving someone a float and telling them "you can use this like a rational number from math". And in fact it might work for the longest time - until it doesn't (think rounding-errors and similar) and then the confusion will be big.
With monads it's the same, but potentially even worse, because it's rather easy to test float addition behavior, but for certain monads that can be much harder.
Haskell has a library called monad-validate that I think is a great example for both what the monad laws are important for, and how it's important that one understand their reasons, instead of just following advice literally on every possible situation.
Just as yo want to break addition commutativity so you can approximate real numbers, there are times when you want something to behave like a monad, but not follow its laws.
It gives a lot of theoretical background, but if you read the article here and already know the Optional-type and then you can skip it and go directly to "A Real World Example".
thanks! this link should have been the main news; so much easier to think of 'unit' as object constructor, and of 'bind' as flatmap; the article also does a great job of explaining the purpose of the monad laws.
Monads should satisfy the "monad laws": left identity, right identity, and associativity.
IIRC, Haskell relies on two out of the three, but I don't remember which two. But if you write a "monad" that doesn't satisfy them (even though it has the right function signatures), then you're going to get bogus results.
Functor, monoid and monad laws allow for undefined evaluation order, lazy evaluation, parallell execution and same results for same parameters. But only if the laws holds can such be guaranteed when using certain abstractions. Associativity being one obvious caveat that might break abstraction over collections, while using divide & conquer mechanics such as function currying.
Because, a lot of the convenience and ergonomics that we can derive using these abstractions comes from being able to rely on the laws. Without them, they are just not really useful.
And when looking at the examples, I can totally see that someone reads
> That's all Monad is. It is an implementation of some "method" that conforms to that interface specification.
and then makes "Set" or "HashMap" implement the interface unlawful and bad things happen when they are least expected.