在使用monad变换器构建monad堆栈来编写库时,我遇到了一个关于它的行为的问题。
以下代码无法通过类型检查程序:
{-# 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
错误是:
$ 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 }
修正很容易,因为GHC建议,只需在Monad
函数中添加foo
约束:
foo :: Monad m => FooM m Int
foo = do
x <- ask
return x
但是在这里,仅foo
函数ask
的{{1}}值才能提供其FooM
值,而且它已经是(自动派生的)Int
实例。
因此,我认为MonadReader
不需要Monad
约束。
我想这与monad变换器的实现有关(我使用mlt==2.2.1), 但我无法弄清楚具体原因。 我可能会错过一些明显的东西。 你能解释一下为什么它没有通过检查器吗?
感谢。
答案 0 :(得分:10)
这是因为Monad
的{{1}}个实例被定义为
ReaderT
即。只有当内容instance Monad m => Monad (ReaderT r m)
是ReaderT r m
的实例时,类型Monad
才是m
的实例。这就是为什么在使用Monad
m
Monad
实例(ReaderT
类型通过派生机制使用)时,您无法获得无约束FooM
的原因。
答案 1 :(得分:5)
return
s type为Monad m => a -> m a
,因此需要约束。
答案 2 :(得分:5)
根据monad法律foo ≡ ask
,它确实可以在没有Monad
约束的情况下工作。但是如果你不需要Monad
,那么GHC很难根据这些法则进行简化,是吗?在类型检查代码之前,当然不是。你写的是
foo = ask >>= \x -> return x
需要(>>=) :: Monad (FooM m) => FooM m Int -> (Int->FooM m Int) -> FooM m Int
和return :: Monad (FooM m) => Int->FooM m Int
。
同样,>>= return
对正确的monad没有任何作用,但对于非monad,它甚至没有定义,因此不能被忽略。