I have type Foo
which is simple wrapper around Cont a a
. I would like to make Foo
type an instance of Monad
class. I try this:
import Control.Monad.Cont
newtype Foo a = Foo {unFoo :: Cont a a}
instance Monad Foo where
return = Foo . return
Foo inner >>= func = Foo (inner >>= newFunc)
where newFunc x = (unFoo $ func x)
But I got this error:
Couldn't match type `a' with `b'
`a' is a rigid type variable bound by
the type signature for >>= :: Foo a -> (a -> Foo b) -> Foo b
at Classes.hs:7:5
`b' is a rigid type variable bound by
the type signature for >>= :: Foo a -> (a -> Foo b) -> Foo b
at Classes.hs:7:5
Expected type: ContT b Data.Functor.Identity.Identity a
Actual type: Cont a a
In the first argument of `(>>=)', namely `inner'
In the first argument of `Foo', namely `(inner >>= newFunc)'
In the expression: Foo (inner >>= newFunc)
How to add Monad
instance for Foo
correctly?
You can't make Foo
into a Monad
.
First, let's point out that Foo a
is an elaborate way of writing (a -> a) -> a
.
runFoo :: Foo a -> ((a -> a) -> a)
runFoo = runCont . unFoo
foo :: ((a -> a) -> a) -> Foo a
foo = Foo . cont
There's only one way we can define (>>=) :: Foo a -> (a -> Foo b) -> Foo b
. We need an a
to pass to the arrow a -> Foo b
. The only thing we have that has any a
s is a Foo a
, which is equivalent to (a -> a) -> a
. It will give us an a
if we can provide a function of type a -> a
, which there's only one of, id
. So our only choice for how to get an a
is to pass id
.
instance Monad Foo where
return = Foo . return
ma >>= f = f (runFoo ma id)
This will fail one of the Monad
laws, m >>= return ≡ m
. We will write a counter example that lives in Foo
.
counterExample :: Foo Int
counterExample = foo (\f -> if f 0 == 1 then 7 else 13)
The counter example results in 13
when passed the identity function id
, but only 7
when passed the successor function (+1)
.
print $ runFoo counterExample id
print $ runFoo counterExample (+1)
13
7
Due to the Monad
laws, counterExample' = counterExample >>= return
should be exactly the same as counterExample
, but it can't be. >>=
already passed id
to the function and only return
ed the result, 13
. counterExample'
doesn't do the same thing as counterExample
.
let counterExample' = counterExample >>= return
print $ runFoo counterExample' id
print $ runFoo counterExample' (+1)
13
14
Since there was only one possible implementation of >>=
and it isn't correct there is no correct Monad
instance for Foo
.