Reputation: 468
While building a monad stack with monad transformers to write a library, I hit a question about the behavior of it.
The following code won't pass the type checker:
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module Foo (FooM, runFooM, foo) where
import Control.Applicative
import Control.Monad.Reader
newtype FooM m a = FooM { runFooM :: ReaderT Int m a }
deriving (Functor, Applicative, Monad, MonadReader Int)
foo :: FooM m Int
foo = do
x <- ask
return x
The error is:
$ ghc foo.hs
[1 of 1] Compiling Foo ( foo.hs, foo.o )
foo.hs:12:3:
No instance for (Monad m) arising from a do statement
Possible fix:
add (Monad m) to the context of
the type signature for foo :: FooM m Int
In a stmt of a 'do' block: x <- ask
In the expression:
do { x <- ask;
return x }
In an equation for ‘foo’:
foo
= do { x <- ask;
return x }
The fix is easy as GHC suggests, just adds Monad
constraint to the foo
function:
foo :: Monad m => FooM m Int
foo = do
x <- ask
return x
But here, the foo
function only ask
s the FooM
value to give its Int
value and it is already an (automatically derived) MonadReader
instance.
So I think Monad
constraint is not required to m
.
I guess this relates to the implementation of the monad transformers (I use mlt==2.2.1), but I cannot figure out the exact reason. I may miss something obvious though. Could you explain why this doesn't pass the checker?
Thanks.
Upvotes: 1
Views: 170
Reputation: 120711
By the monad laws, foo ≡ ask
, which will indeed work without the Monad
constraint. But if you don't require Monad
, then GHC can hardly make simplifications based on these laws, can it? Certainly not before type checking the code. And what you wrote is syntactic sugar for
foo = ask >>= \x -> return x
which requires both (>>=) :: Monad (FooM m) => FooM m Int -> (Int->FooM m Int) -> FooM m Int
and return :: Monad (FooM m) => Int->FooM m Int
.
Again, the >>= return
does nothing whatsoever for a correct monad, but for a non-monad it isn't even defined and can thus not just be ignored.
Upvotes: 5
Reputation: 24802
It's because the Monad
instance for ReaderT
is defined as
instance Monad m => Monad (ReaderT r m)
i.e. the type ReaderT r m
is an instance of Monad
only if the inne rm
is an instance of Monad
. That's why you cannot have an unconstrained m
when using the Monad
instance of ReaderT
(which your FooM
type is using via the deriving mechanism).
Upvotes: 10
Reputation: 7360
return
s type is Monad m => a -> m a
, hence the need for the constraint.
Upvotes: 5