import Data.Monoid import System.Environment ( getArgs ) data Flag a = Set a | NotSet deriving (Eq, Show) data Config = Config { verbose :: Flag Bool , options :: [String] } deriving (Eq, Show) instance Monoid (Flag a) where mempty = NotSet _ `mappend` (Set x) = Set x x `mappend` NotSet = x instance Monoid Config where mempty = defaultConfig c1 `mappend` c2 = Config (verbose c1 `mappend` verbose c2) (options c1 `mappend` options c2) defaultConfig :: Config defaultConfig = Config NotSet [] -- | Table (associative list) containing all known command line arguments. -- | For all arguments, the list stores a function to apply to the full -- | argument string to process the right sight of '=' (if applicable). configTable :: [ (String, String -> Config) ] configTable = [ ("-v", \_ -> defaultConfig { verbose = Set True} ) , ("-q", \_ -> defaultConfig { verbose = Set False} ) , ("-opt", \x -> defaultConfig { options = (:[]) . tail $ dropWhile (/= '=') x} ) ] -- | Process a single command line option. -- | Try to look it up in the 'configTable'. If found, the corresponding -- | function is applied, if not, default config is used. processArg :: String -> Config processArg arg = let opt = takeWhile (/= '=') arg in case lookup opt configTable of Nothing -> defaultConfig Just f -> f arg -- | This is entry point of our program. First, we get commandline arguments. -- | Then we process each of them separately with 'processArg'. -- | Last, we 'mconcat' them with our monoid instance to get merged flags. main :: IO () main = getArgs >>= print . mconcat . map processArg