The options package lets library and application developers easily work with command-line options.
This document discusses a handful of the utilities in this package. See the API documentation for the Options module for more.
Introductory example
The following example is a full program that can accept two options, --message and --quiet:
import Control.Applicative import Options data MainOptions = MainOptions { optMessage :: String , optQuiet :: Bool } . instance Options MainOptions where defineOptions = pure MainOptions <*> simpleOption "message" "Hello world!" "A message to show the user." <*> simpleOption "quiet" False "Whether to be quiet." main :: IO () main = runCommand $ \opts args -> if optQuiet opts then return () else putStrLn (optMessage opts)
$ ./hello Hello world! $ ./hello --message='ciao mondo' ciao mondo $ ./hello --quiet $
In addition, this library will automatically create documentation options such as --help and --help-all:
$ ./hello --help Help Options: -h, --help Show option summary. --help-all Show all help options. Application Options: --message :: text A message to show the user. default: "Hello world!" --quiet :: bool Whether to be quiet. default: false
Defining options
optionType_bool
optionType_bool stores an option as a Bool. The option's value must be either "true" or "false".
Boolean options are unary, which means that their value is optional when specified on the command line. If a flag is present, the option is set to True.
$ ./app -q $ ./app --quiet
Boolean options may still be specified explicitly by using long flags with the --flag=value format. This is the only way to set a unary flag to "false".
$ ./app --quiet=true $ ./app --quiet=false
optionType_enum
optionType_enum store an option as one of a set of possible values. The type must be a bounded enumeration, and the type's Show instance will be used to implement the parser.
This is a simplistic implementation, useful for quick scripts. Users with more complex requirements for enum parsing are encouraged to define their own option types using optionType.
Example:
data Action = Hello | Goodbye deriving (Bounded, Enum, Show) data MainOptions = MainOptions { optAction :: Action } instance Options MainOptions where defineOptions = pure MainOptions <*> defineOption (optionType_enum "action") (\o -> o { optionLongFlags = ["action"] , optionDefault = Hello }) main = runCommand $ \opts args -> do putStrLn ("Running action " ++ show (optAction opts))
$ ./app Running action Hello $ ./app --action=Goodbye Running action Goodbye
Parsing argument lists
parseOptions
parseOptions attempts to convert a list of command-line arguments into an options value. This can be used by application developers who want finer control over error handling, or who want to perform additional validation on the options value.
The argument list must be in the same encoding as the result of System.Environment.getArgs.
Use parsedOptions, parsedArguments, parsedError, and parsedHelp to inspect the result of parseOptions.
Example:
getOptionsOrDie :: Options a => IO a getOptionsOrDie = do argv <- System.Environment.getArgs let parsed = parseOptions argv case parsedOptions parsed of Just opts -> return opts Nothing -> case parsedError parsed of Just err -> do hPutStrLn stderr (parsedHelp parsed) hPutStrLn stderr err exitFailure Nothing -> do hPutStr stdout (parsedHelp parsed) exitSuccess
parseSubcommand
parseSubcommand attempts to convert a list of command-line arguments into a subcommand action. This can be used by application developers who want finer control over error handling, or who want subcommands that run in an unusual monad.
The argument list must be in the same encoding as the result of System.Environment.getArgs.
Use parsedSubcommand, parsedError, and parsedHelp to inspect the result of parseSubcommand.
Example:
runSubcommand :: Options cmdOpts => [Subcommand cmdOpts (IO a)] -> IO a runSubcommand subcommands = do argv <- System.Environment.getArgs let parsed = parseSubcommand subcommands argv case parsedSubcommand parsed of Just cmd -> cmd Nothing -> case parsedError parsed of Just err -> do hPutStrLn stderr (parsedHelp parsed) hPutStrLn stderr err exitFailure Nothing -> do hPutStr stdout (parsedHelp parsed) exitSuccess
Running main with options
runCommand
runCommand retrieves System.Environment.getArgs and attempts to parse it into a valid value of an Options type plus a list of left-over arguments.
The options and arguments are then passed to the provided computation.
If parsing fails, this computation will print an error and call exitFailure.
If parsing succeeds, and the user has passed a --help flag, and the developer is using the default help flag definitions, then this computation will print documentation and call exitSuccess.
runSubcommand
runSubcommand is used to run applications that are split into subcommands. Use subcommand to define available commands and their actions, then pass them to this computation to select one and run it.
If the user specifies an invalid subcommand, this computation will print an error and call exitFailure. In handling of invalid flags or --help, runSubcommand acts like runCommand.
Example:
import Control.Applicative import Control.Monad (unless) import Options data MainOptions = MainOptions { optQuiet :: Bool } instance Options MainOptions where defineOptions = pure MainOptions <*> simpleOption "quiet" False "Whether to be quiet." data HelloOpts = HelloOpts { optHello :: String } instance Options HelloOpts where defineOptions = pure HelloOpts <*> simpleOption "hello" "Hello!" "How to say hello." data ByeOpts = ByeOpts { optName :: String } instance Options ByeOpts where defineOptions = pure ByeOpts <*> simpleOption "name" "" "The user's name." hello :: MainOptions -> HelloOpts -> [String] -> IO () hello mainOpts opts args = unless (optQuiet mainOpts) $ do putStrLn (optHello opts) bye :: MainOptions -> ByeOpts -> [String] -> IO () bye mainOpts opts args = unless (optQuiet mainOpts) $ do putStrLn ("Good bye " ++ optName opts) main :: IO () main = runSubcommand [ subcommand "hello" hello , subcommand "bye" bye ]
$ ./app hello Hello! $ ./app hello --hello='Allo!' Allo! $ ./app bye Good bye $ ./app bye --name='Alice' Good bye Alice