在其他一些 monad m1 中绑定一个 monadic 值 (m2 a)
bind a monadic value (m2 a) inside some other monad m1
今天在 Coding Dojo 工作,我尝试了以下方法
example :: IO ()
example = do input <- getLine
parsed <- parseOnly parser input
...
其中 parseOnly :: Parser a -> Either String a
(来自 attoparsec
)当然编译器抱怨说 Either ..
不是 IO ..
本质上告诉我我正在混合 monads。
当然可以通过
解决
case parseOnly parser input of .. -> ..
我认为这有点不雅。另外我的猜测是其他人早些时候遇到过这个问题,我认为解决方案与 monad 转换器有关,但最后一点我无法拼凑起来。
它也让我想起了 liftIO
- 但这是我认为的另一种方式,它解决了解除发生在周围一些 monad 中的 IO 操作的问题(更准确地说 MonadIO
- 比如Snap
中的示例,当一个人想要在获取一些 http 的同时将某些内容打印到 stdout
时。
更普遍的是,这个问题似乎是针对 Monad m1
和(不同的)Monad m2
我怎样才能做类似
example = do a <- m1Action
b <- m2Action
..
一般情况下,您不能。整个 do 块必须是一个特定的 monad(因为 example
需要有一些特定的类型)。如果您可以在该 do 块内绑定任意其他 monad,您将拥有 unsafePerformIO
.
Monad 转换器允许您生成一个 monad,结合多个其他 monad 可以做的事情。但是你必须决定 all 你的 do 块中的动作使用相同的 monad 转换器堆栈来使用它们,它们不是在 do-block 中任意切换 monad 的方法。
您使用 case
的解决方案之所以有效,是因为您有一个特定的已知 monad(任一),它可以从其中提取值。并非所有 monad 都提供此功能,因此在不了解所涉及的特定 monad 的情况下不可能构建通用解决方案。这就是为什么 do 块语法不提供这样的快捷方式。
您需要对该表达式进行 类型检查;就像在纯代码中一样。这里,
... = do a <- act1 -- m1 monad
b <- act2 -- m2 monad
...
de-sugars 至:
... = act1 >>= (\a -> act2 >>= \b -> ...)
>>=
签名:
(>>=) :: Monad m => m a -> (a -> m b) -> m b
外部 bind 专用于 m1
,因此它期望括号内的表达式是类型:a -> m1 b
,而内部绑定是专用于 m2
,因此括号内的表达式将是 a -> m2 b
:
类型
-- outer bind expects ( \a -> m1 b )
act1 >>= (\a -> act2 >>= \b -> ...)
-- inner bind results ( \a -> m2 b )
为了类型检查,你需要一个介于两者之间的签名函数m2 b -> m1 b
;这就是 lift
对 m2
和 m1
单子的 某些 class 所做的:即 m1 ~ t m2
其中 t
是 MonadTrans
:
的实例
lift :: (Monad m, MonadTrans t) => m a -> t m a
一般来说,monad transformer就是为了这种交错。您可以使用 ExceptT
example :: IO (Either String ())
example = runExceptT $ do
input <- liftIO getLine
parsed <- parseOnly parser input
...
请注意,对于某些 a
,parseOnly
必须 return ExceptT String IO a
。或者更好的 ExceptT String m a
对于任何 m
。或者如果你想要 parseOnly
到 return 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
...
今天在 Coding Dojo 工作,我尝试了以下方法
example :: IO ()
example = do input <- getLine
parsed <- parseOnly parser input
...
其中 parseOnly :: Parser a -> Either String a
(来自 attoparsec
)当然编译器抱怨说 Either ..
不是 IO ..
本质上告诉我我正在混合 monads。
当然可以通过
解决 case parseOnly parser input of .. -> ..
我认为这有点不雅。另外我的猜测是其他人早些时候遇到过这个问题,我认为解决方案与 monad 转换器有关,但最后一点我无法拼凑起来。
它也让我想起了 liftIO
- 但这是我认为的另一种方式,它解决了解除发生在周围一些 monad 中的 IO 操作的问题(更准确地说 MonadIO
- 比如Snap
中的示例,当一个人想要在获取一些 http 的同时将某些内容打印到 stdout
时。
更普遍的是,这个问题似乎是针对 Monad m1
和(不同的)Monad m2
我怎样才能做类似
example = do a <- m1Action
b <- m2Action
..
一般情况下,您不能。整个 do 块必须是一个特定的 monad(因为 example
需要有一些特定的类型)。如果您可以在该 do 块内绑定任意其他 monad,您将拥有 unsafePerformIO
.
Monad 转换器允许您生成一个 monad,结合多个其他 monad 可以做的事情。但是你必须决定 all 你的 do 块中的动作使用相同的 monad 转换器堆栈来使用它们,它们不是在 do-block 中任意切换 monad 的方法。
您使用 case
的解决方案之所以有效,是因为您有一个特定的已知 monad(任一),它可以从其中提取值。并非所有 monad 都提供此功能,因此在不了解所涉及的特定 monad 的情况下不可能构建通用解决方案。这就是为什么 do 块语法不提供这样的快捷方式。
您需要对该表达式进行 类型检查;就像在纯代码中一样。这里,
... = do a <- act1 -- m1 monad
b <- act2 -- m2 monad
...
de-sugars 至:
... = act1 >>= (\a -> act2 >>= \b -> ...)
>>=
签名:
(>>=) :: Monad m => m a -> (a -> m b) -> m b
外部 bind 专用于 m1
,因此它期望括号内的表达式是类型:a -> m1 b
,而内部绑定是专用于 m2
,因此括号内的表达式将是 a -> m2 b
:
-- outer bind expects ( \a -> m1 b )
act1 >>= (\a -> act2 >>= \b -> ...)
-- inner bind results ( \a -> m2 b )
为了类型检查,你需要一个介于两者之间的签名函数m2 b -> m1 b
;这就是 lift
对 m2
和 m1
单子的 某些 class 所做的:即 m1 ~ t m2
其中 t
是 MonadTrans
:
lift :: (Monad m, MonadTrans t) => m a -> t m a
一般来说,monad transformer就是为了这种交错。您可以使用 ExceptT
example :: IO (Either String ())
example = runExceptT $ do
input <- liftIO getLine
parsed <- parseOnly parser input
...
请注意,对于某些 a
,parseOnly
必须 return ExceptT String IO a
。或者更好的 ExceptT String m a
对于任何 m
。或者如果你想要 parseOnly
到 return 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
...