在一些其他monad m1

时间:2016-01-20 23:51:47

标签: haskell monads monad-transformers

今天在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
             ..

3 个答案:

答案 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 ) 的功能;这是liftm2 b -> m1 bm2 monad的某个类所做的事情:即m1其中m1 ~ t m2是一个实例t

MonadTrans