Consider this monad isomorphic to the monad (Bool ->) :
data Pair a = P aa instance Functor Pair where fmap f (P xy) = P (fx) (fy) instance Monad Pair where return x = P xx P ab >>= f = P xy where P x _ = fa P _ y = fb
and compose it with Maybe monad:
newtype Bad a = B (Maybe (Pair a))
I argue that Bad cannot be a monad.
Partial evidence:
There is only one way to determine fmap that satisfies fmap id = id :
instance Functor Bad where fmap f (B x) = B $ fmap (fmap f) x
Recall the laws of the monad:
(1) join (return x) = x (2) join (fmap return x) = x (3) join (join x) = join (fmap join x)
To determine return x we have two options: B Nothing or B (Just (P xx)) . It is clear that in order to hope for the return of x from (1) and (2), we cannot throw away x , so we need to choose the second option.
return' :: a -> Bad a return' x = B (Just (P xx))
This leaves a join . Since there are only a few possible inputs, we can make a case for each:
join :: Bad (Bad a) -> Bad a (A) join (B Nothing) = ??? (B) join (B (Just (P (B Nothing) (B Nothing)))) = ??? (C) join (B (Just (P (B (Just (P x1 x2))) (B Nothing)))) = ??? (D) join (B (Just (P (B Nothing) (B (Just (P x1 x2)))))) = ??? (E) join (B (Just (P (B (Just (P x1 x2))) (B (Just (P x3 x4)))))) = ???
Since the output is of type Bad a , the only parameters are B Nothing or B (Just (P y1 y2)) , where y1 , y2 must be selected from x1 ... x4 .
In cases (A) and (B), we have no values of type a , so we are forced to return B Nothing in both cases.
Case (E) is determined by the laws of (1) and (2) of the monad:
To return B (Just (P y1 y2)) in case (E), this means that we must select y1 from x1 or x3 , and y2 from x2 or x4 .
Similarly, this suggests that we must choose y1 from x1 or x2 and y2 from x3 or x4 . Combining the two, we determine that the right-hand side of (E) should be B (Just (P x1 x4)) .
So far so good, but the problem arises when you try to fill in the right sides for (C) and (D).
There are 5 possible right sides for each of them, and none of the combinations work. I have no good arguments so far, but I have a program that exhaustively tests all combinations:
{-