How often do you find yourself in the following situation?

-d

print debugging output

Having written a nice little program, you just want to add one little command line option. Typical example:. So all you want to say (in code) is something like this:

If we were given the '-d' flag on the command line, ... [print/enable debug output]

But what you actually have to do is usually this:

Get a hold of your favourite getopt library. If it's not a standard library, make sure to note that dependency somewhere!

library. If it's not a standard library, make sure to note that dependency somewhere! Find the documentation and look up basic usage.

Specify to the library exactly which options you want to accept, whether they take any arguments, and provide a usage note for the autogenerated help text.

Make a loop in your main routine for parsing all options .

routine for . Finally, in that loop, state that -d enables debugging output.

Seem a bit much? Not that there is anything wrong with the above in principle. These libraries are very useful for writing compilers and faithful reproductions of historic UNIX utilities. But for your everyday script? I think it's annoying.

So, speaking of returns to simplicity. These are for pasting into your next Haskell script:

import System.Environment -- pesco's really cheap and simple flags and options (tm) clparts = getArgs >>= return . (\(a,b) -> (a,drop 1 b)) . break (=="--") getargs = clparts >>= \(a,b)-> return ([h:t| h:t<-a, h/='-' || null t] ++ b) getflags = clparts >>= \(a,_)-> return (concat [t| '-':t <- a]) getflag x = getflags >>= return . elem x getenv f v x = catch (getEnv v >>= return . f) (\_ -> return x)

Here, have some type signatures, too:

getargs :: IO [String] getflags :: IO [Char] getflag :: Char -> IO Bool getenv :: (String -> a) -> String -> a -> IO a

Usage of the functions is pretty apparent from the types. Don't look anything up. Please!

From five lines you get:

Boolean flags : Anywhere in your ( IO ) code, ask whether a certain flag has been specified: d <- getflag 'd' when d $ putStrLn "debug on!" Alternatively, get the list of flags once and pass it around. flags <- getflags when all (`elem` flags) "nlp" $ putStrLn "my favourite options!"

: Anywhere in your ( ) code, ask whether a certain flag has been specified: Alternatively, get the list of flags once and pass it around. Condensed flags : Throw any number of flag characters behind a single dash - : $ myps -aux

: Throw any number of flag characters behind a single dash : Options with defaults : This might feel strange, but seriously: Use environment variables. To query, complete with reader/parser and default value: n <- getenv read "n" 10 -- think "tail -n" c <- getenv read "C" 0 -- think "grep -C" -- default comes in from another IO action today <- getenv parsedate "today" =<< getcurdate -- cascading defaults plan <- getenv id "PLAN" =<< getenv (++ "/.plan") "HOME" =<< return "-" BTW, don't think that setting an env var is more work than a command line argument! Compare: $ today=2010-07-26 watnu vs. $ watnu --today=2010-07-26 The so-called keyword parameter syntax of the former has been supported since the original Bourne Shell.

: This might feel strange, but seriously: Use environment variables. To query, complete with reader/parser and default value: BTW, don't think that setting an env var is more work than a command line argument! Compare: vs. The so-called syntax of the former has been supported since the original Bourne Shell. Retrieve any non-flag arguments via the getargs routine.

via the routine. Arguments that begin with a dash: Anything after an argument of -- is not a flag. A single dash - is also not a flag.

You buy simplicity at the expense of comprehensiveness. What you don't get:

Long flags (e.g. --debug ). You have 52 latin letters (upper- and lower-case) available. Plus any digits and special characters you can sensibly make use of. When you need more, use a real command line option parser. You're writing a goddamn compiler or something.

(e.g. ). You have 52 latin letters (upper- and lower-case) available. Plus any digits and special characters you can sensibly make use of. When you need more, use a real command line option parser. You're writing a goddamn compiler or something. Complaints about unrecognized flags or options. Defining the following function is left as the proverbial exercise to the reader: allowflags :: [Char] -> IO ()

Defining the following function is left as the proverbial exercise to the reader: Command line options with arguments . Use environment variables.

. Use environment variables. Autogenerated help and usage messages . Type them yourself, they're going to be so much prettier. Come on, add some ASCII art. When it really is too bothersome, feel free to define the following function: helpscreen :: String -- textual command description -> [(Char, String)] -- recognized flags with desc. -> [(String, String)] -- recognized env vars with desc. -> String

. Type them yourself, they're going to be so much prettier. Come on, add some ASCII art. When it really is too bothersome, feel free to define the following function: Elaborate error messages. Obviously there's nothing like command line option --foo requires Frob argument, but got Twizzle in those five lines. Then again, nothing is to stop you from putting arbitrarily complicated error checking in the reader you pass to getenv or validate the order and number of things in the result of getflags , etc..

GetOpt

I did make an elaborate command line parsing library a few years ago. Suitable for building big compilers and stuff! You might still find it in the caches of the intarweb if you're interested. Reading the original abstract, the goal was similar then: Make it really easy to get command line options into programs. Alas, using that library still suffered from problems outlined at the top of this post: Find it, look through the docs, add a big bunch of code to your project, use an elaborate API. I think it was a very good replacement for the standard; considerably nicer, with many fancy features. But probably overkill for everyday use in scripts or small programs.