隐式强制?

时间:2014-11-07 14:36:22

标签: haskell monad-transformers newtype

我不明白为什么这段代码会出现问题:

error1 :: ErrorT String (ReaderT Int IO) Int
error1 = asks id

fyi,asks有这种类型:

asks :: Monad m => (r -> a) -> ReaderT r m a

另一方面,我能够理解,这个代码类似于:

reader1 :: ReaderT Int IO Int
reader1 = asks id

id的类型为a -> aMonad的实例为IO,因此编译器可以推断出类型。这对我来说很清楚。

ErrorT是新类型和haskell spec州,(在关于newtypes的部分中):

  

...它创建了一个必须明确强制转换为的独特类型   来自原始类型...

根据我的解释,我应该能够明确地获得与error1 中相同的类型,并且有一些类似的强制:

reader2 :: ReaderT Int IO (Either String Int)
reader2 = fmap (\i -> Right i) reader1

error2 :: ErrorT String (ReaderT Int IO) Int
error2 = ErrorT reader2

但是,显然,由于error1类型检查很好,因此我隐藏了一些知识。你可以帮我揭开它吗?

运行示例代码所需的导入:

import Control.Monad.Error (ErrorT(ErrorT))
import Control.Monad.Reader (ReaderT, asks)

2 个答案:

答案 0 :(得分:9)

函数asks由两个相关模块导出,类型略有不同。来自Control.Monad.Trans.Readertransformers包的一部分)的版本具有问题中给出的类型:

asks :: Monad m => (r -> a) -> ReaderT r m a

但是,使用的版本似乎是来自mtl模块的Control.Monad.Reader包中的版本,该模块具有以下更通用的类型:

asks :: MonadReader r m => (r -> a) -> m a

所以示例定义

error1 :: ErrorT String (ReaderT Int IO) Int
error1 = asks id

表示

MonadReader Int (ErrorT String (ReaderT Int IO))

必须坚持。

mtl还定义了MonadReader的以下实例:

instance Monad m => MonadReader r (ReaderT r m)
instance (Error e, MonadReader r m) => MonadReader r (ErrorT e m)

通过这些,上面的约束减少到

(Error String, Monad IO)

这两个都持有。

答案 1 :(得分:0)

我认为您的部分回答是asks包中的putgetthrowErrormtl等monadic函数是写入以根据monad堆栈的评估方式自动提升自己。

例如,以下功能:

foo = do a <- asks id
         if a < 0 then throwError "oops"
                  else return $ sqrt a

可以有两种类型:

  • ErrorT字符串(ReaderT Double m)Double
  • ReaderT(ErrorT String m Double)Double

取决于runReaderTrunErrorT的运行顺序。

此功能的最常见类型是:

foo :: (MonadError [Char] m, MonadReader b m, Ord b, Floating b) => m b

表明没有对monad图层进行先验排序。

在您的示例中,您提供了一个类型签名,即即使您没有使用throwError函数,也会在您的monad中存在ErrorT图层。这相当于将MonadError [Char] m约束添加到类型签名。