I don't understand why this code typechecks:
error1 :: ErrorT String (ReaderT Int IO) Int
error1 = asks id
fyi, the asks
has this type:
asks :: Monad m => (r -> a) -> ReaderT r m a
On the other hand, I'm able to understand, that this code typechecks:
reader1 :: ReaderT Int IO Int
reader1 = asks id
id
has type a -> a
and there is an instance of Monad
for IO
, so the compiler can infer the type. That's clear for me.
The ErrorT
is newtype and haskell spec states, (in the section about newtypes):
... it creates a distinct type that must be explicitly coerced to or from the original type ...
According to my interpretation, I should be able to get the same type as in error1
only explicitly, with some coercion similar to this:
reader2 :: ReaderT Int IO (Either String Int)
reader2 = fmap (\i -> Right i) reader1
error2 :: ErrorT String (ReaderT Int IO) Int
error2 = ErrorT reader2
But, apparently, since the error1
typechecks just fine, there is some knowledge hidden from me. Can You help uncovering it for me?
The imports needed for running the example code:
import Control.Monad.Error (ErrorT(ErrorT))
import Control.Monad.Reader (ReaderT, asks)
The function asks
is exported by two related modules with slightly different types. The version from Control.Monad.Trans.Reader
(part of the transformers
package), has the type given in the question:
asks :: Monad m => (r -> a) -> ReaderT r m a
However, the version used seems to be the one in the mtl
package, from the Control.Monad.Reader
module, which has the following, more general, type:
asks :: MonadReader r m => (r -> a) -> m a
So the example definition
error1 :: ErrorT String (ReaderT Int IO) Int
error1 = asks id
means that
MonadReader Int (ErrorT String (ReaderT Int IO))
must hold.
Also defined by mtl
are the following instances for MonadReader
:
instance Monad m => MonadReader r (ReaderT r m)
instance (Error e, MonadReader r m) => MonadReader r (ErrorT e m)
With these, the constraint above reduces to
(Error String, Monad IO)
which both hold as well.