
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