2014-08-29 45 views
1

在用monad变换器构建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 s的FooM值赋予它的Int值,它已经是一个(自动派生的)MonadReader实例。 所以我认为Monad约束是不需要m

我想这涉及到monad变压器(我使用mlt==2.2.1), 的实施,但我无法弄清楚确切的原因。 虽然我可能会错过某些明显的东西。 你能解释为什么这不通过检查?

谢谢。

回答

10

这是因为对于ReaderTMonad实例定义为

instance Monad m => Monad (ReaderT r m) 

即类型ReaderT r mMonad实例仅当INNEřmMonad一个实例。这就是为什么当使用ReaderTMonad实例(您的FooM类型通过派生机制使用)时,您不能有无约束的m

+0

啊!这是我错过的答案。非常感谢你! – bicycle1885 2014-09-01 14:31:11

5

按照monad法律,foo ≡ ask,这确实可以在没有Monad限制的情况下工作。但是,如果您不需要Monad,那么GHC很难基于这些规律进行简化,可以吗?当然不是之前类型检查代码。和你写的是

foo = ask >>= \x -> return x 

语法糖既要有(>>=) :: Monad (FooM m) => FooM m Int -> (Int->FooM m Int) -> FooM m Intreturn :: Monad (FooM m) => Int->FooM m Int

同样,>>= return对于正确的monad不做任何事情,但对于非monad,它甚至没有定义,因此不能被忽略。

相关问题