连续monad中优雅的haskell案例/错误处理

Sve*_*cke 6 haskell

因为我之前在其他问题上过于简单,所以我想在这里给出一个更清晰的例子.

如何在不嵌套多个案例的情况下处理必须以顺序方式检查certian条件的情况?使用"顺序方式"我的意思是获取一个值(例如来自stdin),检查该值是否适用于某个条件,并取决于获得另一个值的结果等等.

例:

sequen :: IO String
sequen = do
  a <- getLine
  case a of
    "hi" -> do
      putStrLn "hello!"
      b <- getLine
      case b of
        "how are you?" -> do
          putStrLn "fine, thanks"
          return "nice conversation"
        _ -> return "error 2"
    _ -> return "error 1"
Run Code Online (Sandbox Code Playgroud)

我知道有更好的方法来编写这样的聊天机器人,它应该只是展示问题的顺序性.正如您所看到的,对于每个嵌套的情况,代码也会缩进.

有没有办法更好地构建这样的代码?我正在考虑在一个地方处理"错误"并描述"成功路径"而不分配错误处理.

Gab*_*lez 21

当然.这正是EitherT为此而制造的.你可以从它Control.Monad.Trans.Either的eitherT包装.

import Control.Monad.Trans.Class
import Control.Monad.Trans.Either

main = do
    e <- runEitherT $ do
        a <- lift getLine
        case a of
            "hi" -> lift $ putStrLn "hello!"
            _    -> left 1
        b <- lift getLine
        case b of
            "how are you?" -> lift $ putStrLn "fine, thanks!"
            _              -> left 2
        return "nice conversation"
    case e of
        Left  n   -> putStrLn $ "Error - Code: " ++ show n
        Right str -> putStrLn $ "Success - String: " ++ str
Run Code Online (Sandbox Code Playgroud)

EitherT每当遇到left语句时就中止当前代码块,人们通常使用它来指示错误条件.

内部块的类型是EitherT Int IO String.当你runEitherT这样,你得到IO (Either Int String).所述Left类型对应于在那里它没有用的情况left和Right值意味着它成功地到达该块的末端.

  • 在EitherT的笔记中,我写了一篇关于此的博客文章,这篇文章非常受欢迎:http://ocharles.org.uk/blog/posts/2012-07-24-in-praise-of-EitherT.html (2认同)

csc*_*eid 5

我一段时间地写了一系列帖子,回顾了我自己对Either&EitherT类型的学习.你可以在这里阅读:http://watchchrislearn.com/blog/2013/12/01/working-entirely-in-eithert/

我使用该errors软件包来获得一堆使用EitherT的好帮手(例如left,right函数返回提升版本的Left和Right).

通过将潜在的失败条件提取到自己的帮助程序中,可以使代码的主线完全按顺序读取,而不会检查结果的case语句.

从那篇文章中,你可以看到该runEitherT部分是一个连续的工作块,它恰好具有失败机制EitherT.显然,这段代码是相当专业的,以显示MaybeT内部的播放方式EitherT.在真实的代码中,它只是你想要讲述的故事,只有一个Left/ Right最后.

import Control.Error
import Control.Monad.Trans

-- A type for my example functions to pass or fail on.
data Flag = Pass | Error

main :: IO ()
main = do
  putStrLn "Starting to do work:"

  result <- runEitherT $ do
      lift $ putStrLn "Give me the first input please:"
      initialText <- lift getLine
      x <- eitherFailure Error initialText

      lift $ putStrLn "Give me the second input please:"
      secondText <- lift getLine
      y <- eitherFailure Pass (secondText ++ x)

      noteT ("Failed the Maybe: " ++ y) $ maybeFailure Pass y

  case result of
    Left  val -> putStrLn $ "Work Result: Failed\n " ++ val
    Right val -> putStrLn $ "Work Result: Passed\n " ++ val

  putStrLn "Ok, finished. Have a nice day"

eitherFailure :: Monad m => Flag -> String -> EitherT String m String
eitherFailure Pass  val = right $ "-> Passed " ++ val
eitherFailure Error val = left  $ "-> Failed " ++ val

maybeFailure :: Monad m => Flag -> String -> MaybeT m String
maybeFailure Pass  val = just $ "-> Passed maybe " ++ val
maybeFailure Error _   = nothing
Run Code Online (Sandbox Code Playgroud)