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