Haskell: Parsing command line arguments

后端 未结 4 1050
耶瑟儿~
耶瑟儿~ 2021-02-02 09:14

This more of a style question, rather than a how to.

So I\'ve got a program that needs two command line arguments: a string and an integer.

I implemented it this

4条回答
  •  广开言路
    2021-02-02 10:07

    I suggest using a case expression:

    main :: IO ()
    main = do
      args <- getArgs
      case args of
        [aString, aInteger] | [(n,_)] <- reads aInteger ->
          doStuffWith aString n
        _ -> do
          name <- getProgName
          hPutStrLn stderr $ "usage: " ++ name ++ "  "
          exitFailure
    

    The binding in a guard used here is a pattern guard, a new feature added in Haskell 2010 (and a commonly-used GHC extension before that).

    Using reads like this is perfectly acceptable; it's basically the only way to recover properly from invalid reads, at least until we get readMaybe or something of its ilk in the standard library (there have been proposals to do it over the years, but they've fallen prey to bikeshedding). Using lazy pattern matching and conditionals to emulate a case expression is less acceptable :)

    Another possible alternative, using the view patterns extension, is

    case args of
      [aString, reads -> [(n,_)]] ->
        doStuffWith aString n
      _ -> ...
    

    This avoids the one-use aInteger binding, and keeps the "parsing logic" close to the structure of the argument list. However, it's not standard Haskell (although the extension is by no means controversial).

    For more complex argument handling, you might want to look into a specialised module — System.Console.GetOpt is in the standard base library, but only handles options (not argument parsing), while cmdlib and cmdargs are more "full-stack" solutions (although I caution you to avoid the "Implicit" mode of cmdargs, as it's a gross impure hack to make the syntax a bit nicer; the "Explicit" mode should be just fine, however).

提交回复
热议问题