今天在Coding Dojo工作我尝试了以下
example :: IO ()
example = do input <- getLine
parsed <- parseOnly parser input
...
其中parseOnly :: Parser a -> Either String a
(来自attoparsec
)当然编译器抱怨Either ..
不是IO ..
基本上告诉我我混合monad。
当然这可以通过
来解决 case parseOnly parser input of .. -> ..
我认为,这是一种不雅观的东西。另外我的猜测是其他人之前遇到过这个问题而且我认为解决方案与monad变换器有关,但最后一点我不能拼凑起来。
它还让我想起了liftIO
- 但这是我想的另一种解决方法,它解决了在一些周围的monad中发生IO动作的问题(更确切地说是MonadIO
- 比如说里面Snap
当想要在获取某些http时将某些内容打印到stdout
时。{<1}}。
更一般的问题似乎是针对Monad m1
和(不同的)Monad m2
这样的问题我该如何做?
example = do a <- m1Action
b <- m2Action
..
答案 0 :(得分:6)
总的来说,你不能。整个do块必须是一个特定的monad(因为example
需要一些特定的类型)。如果你可以绑定一个任意的其他monad,你可以使用unsafePerformIO
。
Monad变形金刚允许你制作一个monad,结合多个其他monad可以做的事情。但你必须决定你的do块中的 all 动作使用相同的monad变换器堆栈来使用它们,它们不是在do-block中任意切换monad的方法。
只有case
的解决方案才有效,因为你有一个特定的已知monad(Either),它有一种从内部提取值的方法。并非所有monad都提供此功能,因此在不了解所涉及的特定monad的情况下构建通用解决方案是不可能的。这就是do block语法不能提供这种快捷方式的原因。
答案 1 :(得分:4)
通常,monad变换器用于这种交错。您可以使用ExceptT
example :: IO (Either String ())
example = runExceptT $ do
input <- liftIO getLine
parsed <- parseOnly parser input
...
请注意parseOnly
必须为某些ExceptT String IO a
返回a
。对于任何ExceptT String m a
,或者更好m
。或者,如果您希望parseOnly
返回Either String a
,则
example :: IO (Either String ())
example = runExceptT $ do
input <- lift getLine
parsed <- ExceptT $ return $ parseOnly parser input
...
但我认为你所需要的只是
eitherToIO :: Either String a -> IO a
eitherToIO (Left s) = error s
eitherToIO (Right x) = return x
parseOnly :: ... -> String -> Either String Int
example :: IO ()
example = do
input <- getLine
parsed <- eitherToIO $ parseOnly parser input
...
答案 2 :(得分:2)
您需要将该表达式设为 type check ;就像纯粹的代码一样。这里,
... = do a <- act1 -- m1 monad
b <- act2 -- m2 monad
...
去糖:
... = act1 >>= (\a -> act2 >>= \b -> ...)
>>=
是签名:
(>>=) :: Monad m => m a -> (a -> m b) -> m b
外部绑定专门用于m1
,因此它期望括号内的表达式为:a -> m1 b
,而内部绑定专用于{{1}所以括号内的表达式将是m2
:
a -> m2 b
对于类型检查,您需要在两者之间使用签名-- outer bind expects ( \a -> m1 b )
act1 >>= (\a -> act2 >>= \b -> ...)
-- inner bind results ( \a -> m2 b )
的功能;这是lift
对m2 b -> m1 b
和m2
monad的某个类所做的事情:即m1
其中m1 ~ t m2
是一个实例t
:
MonadTrans