我不明白为什么这段代码会出现问题:
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 -> a
,Monad
的实例为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)
答案 0 :(得分:9)
函数asks
由两个相关模块导出,类型略有不同。来自Control.Monad.Trans.Reader
(transformers
包的一部分)的版本具有问题中给出的类型:
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
包中的put
,get
,throwError
,mtl
等monadic函数是写入以根据monad堆栈的评估方式自动提升自己。
例如,以下功能:
foo = do a <- asks id
if a < 0 then throwError "oops"
else return $ sqrt a
可以有两种类型:
取决于runReaderT
和runErrorT
的运行顺序。
此功能的最常见类型是:
foo :: (MonadError [Char] m, MonadReader b m, Ord b, Floating b) => m b
表明没有对monad图层进行先验排序。
在您的示例中,您提供了一个类型签名,即即使您没有使用throwError
函数,也会在您的monad中存在ErrorT图层。这相当于将MonadError [Char] m
约束添加到类型签名。