Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Command line arguments reading monad library

I'm looking for a library which uses a monad to abstract over the fuss of command line arguments parsing and help generation. I have the following fairly obvious usage pattern in mind:

main = do
  portOrSocket <- Args.run $ do
    mbSocket <- Args.read $ Args.Arg "s" "socket" "Description"
    mbPort <- Args.read $ Args.Arg "p" "port" "Description"
    case mbSocket of
      Just socket -> return $ Right socket
      Nothing -> case mbPort of
        Just port -> return $ Left port
        Nothing -> return $ Left defaultPort
  ...

The above code has all the information needed to handle parsing, validation and usage generation and IMO is fairly easy to comprehend. Unfortunately after looking thru hackage and checking out packages like cmdargs, cmdlib, parseargs, ReadArgs I haven't found anything anywhere close to this. But before diving into implementation I'd like to ensure that I haven't missed anything. So is there a library that exploits a similar approach to the problem?

like image 966
Nikita Volkov Avatar asked Jun 30 '13 22:06

Nikita Volkov


2 Answers

You can use optparse-applicative. The most common use pattern looks like this (I'm just copy-and-pasting from a small utility I use):

options :: Parser (String, String)
options = (,)
    <$> (strOption $ mconcat [
        short 'n',
        long "node",
        metavar "NODE",
        value "127.0.0.1",
        showDefaultWith id,
        completer (bashCompleter "hostname"),
        help "AMQP node to connect to" ] )
    <*> (strOption $ mconcat [
        short 'q',
        long "queue",
        metavar "QUEUE",
        value "1.0.0",
        showDefaultWith id,
        help "Queue to initialize" ] )

main = do
    (hostName, queue) <-
        execParser $ info (helper <*> options) $ mconcat [
            fullDesc,
            header "The Suns setup utility",
            progDesc "Sets up an AMQP node",
            footer "Report bugs to [email protected]" ]
    ...

When I run the compiled program with -h, I get:

$ suns-admin -h
The Suns setup utility

Usage: suns-admin [-n|--node NODE] [-q|--queue QUEUE]
  Sets up an AMQP node

Available options:
  -h,--help                Show this help text
  -n,--node NODE           AMQP node to connect to (default: 127.0.0.1)
  -q,--queue QUEUE         Queue to initialize (default: 1.0.0)

Report bugs to [email protected]

That gives you some idea of some nifty options that you can play with and the nice output that the program generates.

like image 97
Gabriella Gonzalez Avatar answered Oct 23 '22 19:10

Gabriella Gonzalez


If anybody's interested in solving the problem presented in the question using optparse-applicative, here's how I achieved that:

import Options.Applicative

getOptions :: Int -> IO (Either Int String)
getOptions defaultPort = 
  execParser $ 
  info (helper <*> parser defaultPort) $
    fullDesc <>
    progDesc "Run a content-db server on a socket or a port" <>
    header "Run a content-db server" 

parser :: Int -> Parser (Either Int String)
parser defaultPort = 
  portOrSocket <$> 
    (optional . strOption)
      ( short 's' <>
        long "socket" <>
        help "Socket" )
    <*>
    option
      ( short 'p' <>
        long "port" <>
        help "Port" <>
        value defaultPort )
  where
    portOrSocket (Just socket) _ = Right socket
    portOrSocket _ port = Left port

main = do
  getOptions 43400 >>= \o -> case o of
    Left port -> print port
    Right socket -> print socket
like image 2
Nikita Volkov Avatar answered Oct 23 '22 19:10

Nikita Volkov