

import System.Environment



main = do

[min,max] <- getArgs

print (read min, read max)





data Guess = Guess {min :: Int, max :: Int, limit :: Maybe Int} deriving (Data,Typeable,Show)





{-# LANGUAGE DeriveDataTypeable #-}

import System.Console.CmdArgs



data Guess = Guess {min :: Int, max :: Int, limit :: Maybe Int} deriving (Data,Typeable,Show)



main = do

x <- cmdArgs $ Guess 1 100 Nothing

print x





$ guess --min=10

NumberGuess {min = 10, max = 100, limit = Nothing}



$ guess --min=10 --max=1000

NumberGuess {min = 10, max = 1000, limit = Nothing}



$ guess --limit=5

NumberGuess {min = 1, max = 100, limit = Just 5}



$ guess --help

The guess program



guess [OPTIONS]



-? --help Display help message

-V --version Print version information

--min=INT

--max=INT

-l --limit=INT





guess = cmdArgsMode $ Guess {min = 1, max = 100, limit = Nothing}



main = do

x <- cmdArgsRun guess

print x





guess = cmdArgsMode $ Guess

{min = 1 &= argPos 0 &= typ "MIN"

,max = 100 &= argPos 1 &= typ "MAX"

,limit = Nothing &= name "n" &= help "Limit the number of choices"}

&= summary "Neil's awesome guessing program"





$ guess

Requires at least 2 arguments, got 0



$ guess 1 100

Guess {min = 1, max = 100, limit = Nothing}



$ guess 1 100 -n4

Guess {min = 1, max = 100, limit = Just 4}



$ guess -?

Neil's awesome guessing program



guess [OPTIONS] MIN MAX



-? --help Display help message

-V --version Print version information

-n --limit=INT Limit the number of choices





{-# LANGUAGE DeriveDataTypeable, RecordWildCards #-}



import System.Random

import System.Console.CmdArgs



data Guess = Guess {min :: Int, max :: Int, limit :: Maybe Int} deriving (Data,Typeable)



main = do

Guess{..} <- cmdArgs $ Guess 1 100 Nothing

answer <- randomRIO (min,max)

game limit answer



game (Just 0) answer = putStrLn "Limit exceeded"

game limit answer = do

putStr "Have a guess: "

guess <- fmap read getLine

if guess == answer then

putStrLn "Awesome!!!1"

else do

putStrLn $ if guess > answer then "Too high" else "Too low"

game (fmap (subtract 1) limit) answer



A few days ago I went to the Haskell Hoodlums meeting - an event in London aimed towards people learning Haskell. The event was good fun, and very well organised - professional quality Haskell tutition for free by friendly people - the Haskell community really is one of Haskell's strengths! The final exercise was to write a program that picks a random number between 1 and 100, then has the user take guesses, with higher/lower hints. After writing the program, Ganesh suggested adding command line flags to control the minimum/maximum numbers. It's not too hard to do this directly with getArgs:Next we discussed adding an optional limit on the number of guesses the user is allowed. It's certainly possible to extend the getArgs variant to take in a limit, but things are starting to get a bit ugly. If the user enters the wrong number of arguments they get a pattern match error. There is no help message to inform the user which flags the program takes. While getArgs is simple to start with, it doesn't have much flexibility, and handles errors very poorly. However, for years I used getArgs for all one-off programs - I found the other command line parsing libraries (including GetOpt) added too much overhead, and always required referring back to the documentation. To solve this problem I wrote CmdArgs To start using CmdArgs we first define a record to capture the information we want from the command line:For our number guessing program we need a minimum, a maximum, and an optional limit. The deriving clause is required to operate with the CmdArgs library, and provides some basic reflection capabilities for this data type. Once we've written this data type, a CmdArgs parser is only one function call away:Now we have a simple command line parser. Some sample interactions are:Our simple CmdArgs parser is probably sufficient for this task. I doubt anyone will be persuaded to use my guessing program without a fancy iPhone interface. However, CmdArgs provides all the power necessary to customise the parser, by adding annotations to the input value. First, we can modify the parser to make it easier to add our annotations:We have changed Guess to use record syntax for constructing the values, which helps document what we are doing. We've also switched to using cmdArgsMode/cmdArgsRun (cmdArgs which is just a composition of those two functions) - this helps avoid any problems with capturing the annotations when running repeatedly in GHCi. Now we can add annotations to the guess value:Here we've specified that min/max must be at argument position 0/1, which more closely matches the original getArgs parser - this means the user is always forced to enter a min/max (they could be made optional with the opt annotation). For the limit we've added a name annotation to say that we'd like the flag -n to map to limit, instead of using the default -l. We've also given limit some help text, which will be displayed with --help. Finally, we've given a different summary line to the program.We can now interact with our new parser:For completeness sake, here is the complete program. I think for this program the most suitable CmdArgs parser is the simpler one initially written, which I have used here:(The code in this post can be freely reused for any purpose, unless you are porting it to the iPhone, in which case I want 10% of all revenues.)