Copyright © 2007 Dave Bayer. Subject to a BSD-style license.

This module is part of the Annote project.

module GetOpt

GetOpt handles command line options. It is a wrapper around System.Console.GetOpt.

The modules Options, Convert, and Main demonstate its use.

This design was influenced by the Haskell Cafe thread Avoiding boilerplate retrieving GetOpt cmd line args. See also the Haskell Wiki page GetOpt.

System.Console.GetOpt is a Haskell port of GNU getopt. We import the following functions:

getOpt :: ArgOrder a -> [OptDescr a] -> [String] -> ([a], [String], [String])
usageInfo :: String -> [OptDescr a] -> String
import System.Console.GetOpt

System.IO is the standard IO library. We import the following functions:

hPutStr :: Handle -> String -> IO ()
hPutStrLn :: Handle -> String -> IO ()
import System.IO (stdout,stderr,hPutStr,hPutStrLn)

System.Exit handles exiting the program. We import the following function:

exitWith :: ExitCode -> IO a
import System.Exit (ExitCode(..),exitWith)

OptVal, ArgType, OptionList, OptionSpecs

We maintain option settings as association lists.

type OptVal a = (a,String)
type ArgType a = a → String → ArgDescr (OptVal a)
type OptionList a = [OptVal a]
type OptionSpecs a = [OptDescr (OptVal a)]

noArg, reqArg, optArg

noArg, reqArg, optArg are helper functions for use in constructing an OptDescr list. While their constructor counterparts NoArg, ReqArg, and OptArg have distinct types, we give these the consistent type ArgType for ease of handling.

noArg, reqArg, optArg :: ArgType a

noArg x _ = NoArg (x,"")

reqArg x s = ReqArg f s
    where f y = (x,y)

optArg x s = OptArg f s
    where f (Just y) = (x,y)
          f Nothing  = (x,"")


makeOptions creates an OptionSpecs list from a list of option specification tuples. A sample list entry is

(g,c,s,h,t,m) = (DateFormat, 'd', "date", reqArg, "format", "Date format")


makeOptions :: [(a,Char,String,ArgType a,String,String)] → OptionSpecs a
makeOptions xs = map f xs
    where f (g,c,s,h,t,m) = Option [c] [s] (h g t) m


parseOptions calls getOpt to process the command line arguments. It is adapted from Don Stewart's example,

Programming Haskell: argument handling and a complete cat

parseOptions ::
    Eq a ⇒ [String] → OptionList a → String → OptionSpecs a
    → a → String → a
    → IO (OptionList a,[String])
parseOptions argv defaults usage flags version versionStr help =
    case getOpt Permute flags argv of
        (args,files,[]) → do
            if isOption version args
                then do hPutStr stdout versionStr
                 else return ()
            if isOption help args
                then do hPutStr stdout (usageInfo usage flags)
                else return ()
            return (args ++ defaults, files)
        (_,_,errs) → do
            hPutStrLn stderr (concat errs ++ usageInfo usage flags)
            exitWith (ExitFailure 1)

isOption, getOption

isOption, getOption access the OptVal association list. For getOption, the first match is returned, which is why parseOptions appends default values to the association list.

isOption :: Eq a ⇒ a → OptionList a → Bool
isOption options assoc =  case lookup options assoc of
    Nothing → False
    Just _  → True

getOption :: Eq a ⇒ a → OptionList a → String
getOption options assoc = case lookup options assoc of
    Nothing → ""
    Just s  → s


getOptionOr substitutes a default for missing or empty strings. This can be useful if the desired default is context-dependent, or with optArg.

getOptionOr :: Eq a ⇒ a → OptionList a → String → String
getOptionOr options assoc def = case lookup options assoc of
    Nothing → def
    Just "" → def
    Just s  → s