8

以前の他の質問で単純化しすぎたので、ここでより明確な例を挙げたいと思います。

複数のケースを入れ子にすることなく、特定の条件を順番にチェックする必要がある状況をどのように処理できますか? 「シーケンシャルな方法」とは、(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"

このようなチャットボットを作成するためのより良い方法があることは知っていますが、それは問題のシーケンシャルな性質を示しているだけです。ご覧のとおり、ネストされたすべてのケースで、コードもより深くインデントされます。

そのようなコードをより適切に構造化する方法はありますか? 「エラー」を 1 か所で処理し、エラー処理を分散させずに「成功パス」を記述することを考えています。

4

5 に答える 5

22

もちろん。これはまさにその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

EitherTステートメントに遭遇するたびに現在のコードブロックを中止し、left通常、これを使用してエラー状態を示します。

内部ブロックのタイプはEitherT Int IO Stringです。あなたがrunEitherTそれをするとき、あなたは得るIO (Either Int String)。タイプはLefta で失敗した場合に対応しleftRight値はブロックの最後に正常に到達したことを意味します。

于 2012-11-06T14:35:05.400 に答える
2

あなたは必然的にモナドの中にいるので、の上にエラーモナドを積み重ねるよりも、モナドのエラー処理機能IOを使ったほうがよいでしょう。重いingをすべて回避します。IOIOlift

import Control.Monad ( unless )
import Control.Exception ( catch )
import Prelude hiding ( catch )
import System.IO.Error ( ioeGetErrorString )

main' = do
  a <- getLine
  unless (a == "hi") $ fail "error 1"
  putStrLn "hello!"
  b <- getLine
  unless (b == "how are you?") $ fail "error 2"
  putStrLn "fine, thanks"
  return "nice conversation"

main = catch main' $ return . ioeGetErrorString

この場合、エラーは単純にでStringあり、 によってスローされ、. 他のタイプをスローしたい場合は、代わりにを使用する必要があります。IOfailuserErrorthrowIOfail

于 2012-11-06T16:31:28.447 に答える
0

警告: 仲間の Haskell 初心者が答えます。

Maybe モナドを使えば、この種の階段を避けることができます。この章の冒頭の良い例

ただし、エラーコードを返すため、モナドのどちらか (おそらく存在する) に似たものが必要です。

基本的な考え方は、「Left 1」エラーが発生すると、以降のステップをすべて省略してしまうというものです (遅延評価のため)。

于 2012-11-06T14:35:04.307 に答える