Skip to main content
 首页 » 编程设计

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

2025年05月04日16mengfanrong

我正在尝试编写一个程序,在命令行上接受两个整数并用它们做一些有趣的事情。我想尽可能简单和强制地编写整数的读取/解析,因为它应该是相对简单的代码。

我面临的问题是在 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 

这是我想在命令式伪 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) 

在 Python 中,您基本上可以从上到下阅读主函数,并且所有错误处理都很简单。 有没有办法在 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 

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

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

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

请您参考如下方法:

我是这样写的(不使用任何外部库)

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 

当然,有趣的是 validateArgs。首先我们做一个单一的模式匹配,但从那时起我们只使用 maybe 组合器来很好地抽象出我们的模式匹配。这导致更清晰的代码 IMO。 maybe 组合器采用默认值 b 和延续 a -> b 并将 Maybe a 展开为 b。在这种情况下,我们的默认值是解析失败,我们的延续将 a 注入(inject)到 IO monad 中。