[Haskell-cafe] GetOpt

Tomasz Zielonka tomasz.zielonka at gmail.com
Thu Apr 27 04:37:56 EDT 2006


On Thu, Apr 27, 2006 at 02:26:22AM +0300, Einar Karttunen wrote:
> On 26.04 11:29, Anton Kulchitsky wrote:
> > I just started to study Haskell and it is my almost first big experience 
> > with functional languages (except Emacs Lisp and Python). I enjoyed all 
> > small exercises and started a bigger business writing a general utility. 
> > However, I have a problem from the beginning. The utility get some file 
> > and convert it to another format. It is a kind of small compiler. It 
> > also accepts many parameters and behaves depending on them. The problem 
> > is how to do this neat! How should I write my program to accept and 
> > neatly work with options????
> 
> One solution is to have a datatype for configuration:
> 
> > data Config = Config { mode    :: Mode,
> >                        infile  :: Maybe FilePath,
> >                        outfile :: Maybe FilePath
> >                      }
> > nullConfig = Config Normal "-" "-"
> > data Mode   = Normal | Version | Help
> 
> and handle options as functions from Config to Config:
> 
> > Option ['i']   ["input"]   (ReqArg (\x c -> c { infile = Just x }) "file") "input file name"

I find this approach very convenient, but I push it a bit further. Some
time ago I wrote a small article about this:

    http://www.haskell.org/pipermail/haskell/2004-January/013412.html

I was not the first one to use the approach but I felt that it should be
made more popular. Perhaps I should make a wiki page from it, but I seem
to never do such things and can't promise to do it this time :-/

> and then handle the parsed options like:
> 
> > case conf of
> >   Config Normal (Just i) (Just o) -> ...
> >   Config Normal _        _        -> both input and output must be specified
> >   Config Help   _        _        -> help message

You can eliminate this pattern matching by using functions and
IO-actions as fields of Config, for example:

> data Config = Config { input   :: IO String, -- or (Handle -> IO a) -> IO a
>                        output  :: String -> IO ()
>                      }

This way it is easy to read from stdin and write to stdout by default.

We eliminate Version and Help modes by using IO functions as option
handlers, which enables us to finish the execution in the middle of
option processing.

> Option ['h'] ["help"]   (NoArg (\_ -> printHelp >> exitWith ExitSuccess)) "show help"

Your main function could look like this:

> main = do
>     args <- getArgs
>     let (optsActions, rest, errors) = getOpt RequireOrder options args
>     mapM_ (hPutStrLn stderr) errors
>     config <- foldl (>>=) (return initialConfig) optsActions
>     cs <- input config
>     ...
>     output config result

Best regards
Tomasz


More information about the Haskell-Cafe mailing list