This question comes from this answer in example of a functor that is Applicative but not a Monad: It is claimed that the
data PoE a = Empty | Pair a a deriving (
Apparently, it is not a monad. One of the monad "join
" laws is
join . join = join . fmap join
Hence, according to the law above, these two outputs should be equal, but they are not.
main :: IO ()
main = do
let x = Pair (Pair (Pair 1 2) Empty) (Pair Empty (Pair 7 8))
print (join . join $ x)
-- output: Pair 1 8
print (join . fmap join $ x)
-- output: Empty
The problem is that
join x = Pair (Pair 1 2) (Pair 7 8)
fmap join x = Pair Empty Empty
Performing an additional join
on those does not make them equal.
how to find that out systematically?
join . join
has type m (m (m a)) -> m (m a)
, so I started with a triple-nested Pair
-of-Pair
-of-Pair
, using numbers 1..8
. That worked fine. Then, I tried to insert some Empty
inside, and quickly found the counterexample above.
This approach was possible since a m (m (m Int))
only contains a finite amount of integers inside, and we only have constructors Pair
and Empty
to try.
For these checks, I find the join
law easier to test than, say, associativity of >>=
.