【发布时间】:2016-09-09 12:03:12
【问题描述】:
假设我有一些 Monadic 类型
data Example a = CheckIt {unwrap :: a}
instance Functor Example where
fmap f (CheckIt x) = CheckIt (f x)
instance Applicative Example where
pure = CheckIt
CheckIt f <*> CheckIt x = CheckIt (f x)
instance Monad (Example) where
return = CheckIt
e@(CheckIt a) >>= f = f a
我有一个函数可以根据一些输入返回[a]:
fetchList :: b -> [Int]
fetchList _ = [1,2,3,4]
还有一个返回 IO [a] 的函数(从实际实现中简化):
basicIoWrap :: [a] -> IO [a]
basicIoWrap x = return x
我希望运行这个 IO,然后从中提取一个示例 [a]:
以下不起作用:
foo :: b -> Example [a]
foo val = (basicIoWrap (fetchList val)) >>= \list -> return list
抱怨不同的单子类型
Couldn't match type ‘IO’ with ‘Example’
Expected type: Example [a]
Actual type: IO [Int]
所以我知道这正是 Monad 转换器的用例,但我真的很难弄清楚如何在上下文中应用它们。假设我有一个变压器:
newtype ExampleT m a = ExampleT {runExampleT :: m (Example a)}
我将我的签名改写为 foo :: b -> ExampleT (IO [a])
我不清楚函数体会是什么样子,以及我最终如何从中提取示例 [a]? runExampleT ExampleT (IO [a]) 不会给出IO [Example a],从而简单地将多单子问题推到更远的地方吗?
【问题讨论】:
-
缺少各种不安全的“转义”函数,您无法从 IO monad 中删除列表以返回
Example [a]。没有IoT转换器的原因是因为IO monad,如果使用,必须在堆栈的底部,这意味着任何堆栈的最外面的monad 是IO。 -
那么这种情况一般是怎么处理的呢?
-
monad 转换器主要是向现有 monad 添加行为,而不是从中提取值。如果
foo返回一个IO (Example [a])(正如@user2297560 击败我的建议),那么编写一个bar :: Example [a] -> IO [a]类型的函数来检索内部列表(同时将其保存在IO 上下文中)相对容易。有了这样一个函数,你就可以写foo >>= bar。
标签: haskell monads monad-transformers