1

I've seen some approaches about parsing program arguments. They seem to be too complicated. I need a simple solution. But I also want to be able (preferably, not necessarily) to refer to the arguments by name. So if a command like looks like this:

./MyApp --arg1Name arg1Value --arg2Name arg2Value

then I'd like to treat them as args["arg1Name"] and args["arg2Name"] to get their values. I know that's not a valid Haskell code, though. What I have now is not much:

main = do
  [args] <- getArgs

I repeat, I'd like a simple solution, preferably without involving any third-party haskell libraries.

3
  • 1
    Not so easy. Some arguments are meant to be followed by values and others are not, and the arguments parsing code needs to know which is which. Commented Jul 9, 2014 at 6:00
  • @n.m., in my cause all the argument followed by the values. Commented Jul 9, 2014 at 6:10
  • 3
    Try parse xs = Data.Map.fromList . filter ((== "--") . take 2 . fst) $ zip xs (tail xs). No syntax error checks but hey, what do you want from a one-liner. Commented Jul 9, 2014 at 7:12

2 Answers 2

5

optparse-applicative is great for argument parsing, and very easy to use! Writing your own argument parser will be much more difficult to get right, change, extend, or otherwise manage than if you take 10 minutes to write a parser with optparse-applicative.

Start by importing the Options.Applicative module.

import Options.Applicative

Next create a data type for your command-line configuration.

data Configuration = Configuration
                     { foo :: String
                     , bar :: Int
                     }

Now for the workhorse, we create a parser by using the combinators exported from optparse-applicative. Read the documentation on Options.Applicative.Builder for the full experience.

configuration :: Parser Configuration
configuration = Configuration
            <$> strOption
                ( long "foo"
               <> metavar "ARG1"
                )
            <*> option
                ( long "bar"
               <> metavar "ARG2"
                )

Now we can execute our Parser Configuration in an IO action to get at our command-line data.

main :: IO ()
main = do 
          config <- execParser (info configuration fullDesc)
          putStrLn (show (bar config) ++ foo config)

And we're done! You can easily extend this parser to support a --help argument to print out usage documentation (make a new parser with helper <*> configuration and pass it to info), you can add default values for certain arguments (include a <> value "default" clause in the arguments to strOption or option), you can support flags, or sub-parsers or generate tab-completion data.

Libraries are a force multiplier! The investment you make in learning the basics of a good library will pay dividends in what you're able to accomplish, and tasks will often be easier (and quicker!) with the proper tool than with a "fast" solution thrown together out of duct tape.

Sign up to request clarification or add additional context in comments.

1 Comment

The option now needs an Option Reader. auto offers such functionality and the according line has to be changed as follows: <*> option to <*> option auto
3

How about just parsing them in pairs and adding them to a map:

simpleArgsMap :: [String] -> Map String String
simpleArgsMap [] = Map.empty
simpleArgsMap (('-':'-':name):value:rest)
    = Map.insert name value (simpleArgsMap rest)
simpleArgsMap xs = error $ "Couldn't parse arguments: " ++ show xs

A simple wrapper program to show this working:

module Args where

import System.Environment ( getArgs )
import Control.Applicative ( (<$>) )

import qualified Data.Map as Map
import Data.Map ( Map )

main = do
  argsMap <- simpleArgsMap <$> getArgs
  print $ Map.lookup "foo" argsMap

4 Comments

is there a necessity to import Map 2 times? import qualified Data.Map as Map import Data.Map ( Map )`?
It's a fairly standard pattern. You can leave out import Data.Map ( Map ) but then you have to refer to the type as Map.Map.
by the way, in terms of naming convention, is the space between Map and the braces necessary import Data.Map ( Map ) , is this acceptable import Data.Map(Map) in the eyes of most haskell developers?
Yes, it's fine, and probably more normal actually - I just happen to mainly work on a codebase (Darcs) that uses the spaces so I default to including them.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.