在Haskell中没有模式匹配的错误处理

ill*_*out 1 python haskell coding-style control-structure pattern-matching

我正在尝试编写一个程序,它在命令行中使用两个整数并对它们执行一些有趣的操作.我想尽可能容易和必要地编写整数的读/解,因为它应该是相对简单的代码.

我面临的问题是,在Haskell中处理错误并不是那么简单.似乎在Haskell中经常使用模式匹配.这似乎使得代码比命令式版本更难以遵循.

该程序将像这样运行(在这个例子中,它只是将两个数字加在一起):

$ ./my_prog
ERROR: Must run like `./my_prog NUM_A NUM_B`.
$ ./my_prog cat 1
ERROR: Could not parse NUM_A "cat" as integer
$ ./my_prog 10 dog
ERROR: Could not parse NUM_B "dog" as integer
$ ./my_prog 10 1
11
Run Code Online (Sandbox Code Playgroud)

这是我想要在命令式伪Python中做的事情:

function die (errorMessage):
    print("ERROR: %s" % errorMessage)
    sys.exit(1)

function main ():
    if len(sys.argv) != 2:
        die("Must run program like `%s NUM_A NUM_B`" % sys.progname)

    num_a = 0
    num_b = 0

    try:
        num_a = int(sys.argv[0])
    except:
        die("Could not parse NUM_A \"%s\" as integer" % sys.argv[0])

    try:
        num_b = int(sys.argv[1])
    except:
        die("Could not parse NUM_B \"%s\" as integer" % sys.argv[1])

     doSomethingInteresting(num_a, num_b)

function doSomethingInteresting (num_a, num_b):
    print(num_a + num_b)
Run Code Online (Sandbox Code Playgroud)

在python中,你基本上可以从上到下阅读main函数,所有的错误处理都很简单. 有没有办法在Haskell中实现这种简单,直接的错误处理而不进行多种模式匹配?

这是我提出的Haskell代码执行相同的任务,但由于多个模式匹配部分,它似乎比Python代码复杂得多.

module Main ( main
            )
            where

import System.Environment (getArgs, getProgName)
import System.Exit (ExitCode(..), exitWith)
import Text.Read (readMaybe)

die :: String -> IO a
die err = do putStrLn $ "ERROR: " ++ err
             exitWith (ExitFailure 1)

main :: IO ()
main = do
        args <- getArgs
        progName <- getProgName
        case args of
            [strNumA, strNumB] -> do
                let maybeNumA = readMaybe strNumA :: Maybe Int
                    maybeNumB = readMaybe strNumB :: Maybe Int
                checkMaybeArgs strNumA maybeNumA strNumB maybeNumB
            _ -> die ("Must run like `" ++ progName ++ " NUM_A NUM_B`.")
    where
        checkMaybeArgs :: String -> Maybe Int -> String -> Maybe Int -> IO ()
        checkMaybeArgs badStrNumA Nothing _ _ =
            die ("Could not parse NUM_A \"" ++ badStrNumA ++ "\" as integer")
        checkMaybeArgs _ _ badStrNumB Nothing =
            die ("Could not parse NUM_B \"" ++ badStrNumB ++ "\" as integer")
        checkMaybeArgs _ (Just numA) _ (Just numB) = doSomethingInteresting numA numB

        doSomethingInteresting :: Int -> Int -> IO ()
        doSomethingInteresting numA numB = print $ numA + numB
Run Code Online (Sandbox Code Playgroud)

(如果我的Haskell风格还有其他问题,我将非常感谢任何更正.)

编辑:我最近发现了一篇博客文章,讨论了在Haskell中处理异常的许多不同方法.它有点相关:

http://www.randomhacks.net/articles/2007/03/10/haskell-8-ways-to-report-errors

Dan*_*zer 6

这是我写这个的方式(不使用任何外部库)

import Text.Read
import Text.Printf
import System.Environment
import Control.Monad
import System.Exit


parsingFailure :: String -> String -> IO a
parsingFailure name val = printf "ERROR: Couldn't parse %s : %s as an integer\n" 
                                  name val >> exitWith (ExitFailure 1)

validateArgs :: [String] -> IO (Integer, Integer)
validateArgs [a, b] = liftM2 (,) (parse "VAL A" a) (parse "VAL B" b)
  where parse name s = maybe (parsingFailure name s) return $ readMaybe s
validateArgs _      = putStrLn "Wrong number of args" >> exitWith (ExitFailure 1)

main :: IO ()
main = do
  (a, b) <- getArgs >>= validateArgs
  print $ a + b
Run Code Online (Sandbox Code Playgroud)

当然有趣的是validateArgs.首先我们进行单一模式匹配,但从那时起我们只使用maybe组合器来很好地抽象出模式匹配.这导致了更清晰的代码IMO.该maybe组合子取默认值 b和延续a -> b,并解开Maybe ab.在这种情况下,我们的默认值是解析失败,我们继续注入aIO单子.