应用程序将用户输入作为参数(比如可能是数字ID的文本)。 然后我们想要例如使用该ID从数据库中获取内容。 这两个操作都可能失败(所以我们到达Either)并且都涉及与外部世界的交互(所以IO)。
将monadic动作映射到某个结构 - mapM
,简单!
type E1 = Either String Int
fetchFromDb :: Int -> IO E1
fetchFromDb i = do
return $ case (i `mod` 2 == 0) of
True -> Left "Error - evens not allowed"
False -> Right (100 + i)
main :: IO ()
main = do
n1 <- fetchFromDb 1
n2 <- fetchFromDb 2
n3 <- mapRight fetchFromDb ((Right 3)::E1)
n4 <- mapM fetchFromDb ((Right 4)::E1)
n5 <- mapM fetchFromDb ((Left "No Int at all")::E1)
putStrLn $ "n1 = " ++ (show n1)
putStrLn $ "n2 = " ++ (show n2)
putStrLn $ "n3 = " ++ (show n3)
putStrLn $ "n4 = " ++ (show n4)
putStrLn $ "n5 = " ++ (show n5)
n1 = Right 101
n2 = Left "Error - evens not allowed"
n3 = Right (Right 103)
n4 = Right (Left "Error - evens not allowed")
n5 = Left "No Int at all"
如上所示 - 如果用户提供了错误的Int
,我们会收到您期望的Left
错误。如果我们有一个有效的Int虽然Either
嵌套了(一旦我想到Monad
的{{1}}实例定义有意义了。)
所以 - 需要进行某种减少或折叠,但我不能完全看到如何将它连接起来以及我的初始值是什么。
脚注:在这种情况下,我当然可以使用合适的Either
表达式简单地提取所需的Int,但我正在查看几个参数。
答案 0 :(得分:4)
你有两种方法来处理它。第一种是使用EitherT
而不是Either
,因此您可以将其用作monad。这样您就可以编写monadic代码,Either's
将合并为一个。
第二个(更简单地引入现有代码)使用Control.Monad.join :: Monad m => m (m a) -> m a
,对于嵌套monad(如Eithers
),它只给你一个。