haskelloptparse-applicative

Is it possible to have a optparse-applicative option with several parameters?


I just found out that my carefully crafted parser fails to parse any string I throw at it:

roi :: Parser (Maybe ROI)
roi = optional $ option (ROI <$> auto <*> auto <*> auto <*> auto)
               $ long "roi" <> metavar "ROI" <> help "Only process selected region of interest"

where ROI = ROI Int Int Int Int

If that is important, it is nested in a higher parser

options :: Parser Opts
options = Opts <$> input <*> output <*> roi <*> startT <*> endT  

where Opts is an appropriate ADT.

Now I assumed that the roi parser will parse expressions such as --roi 1 2 3 4 but it fails with Invalid argument '128' and giving me usage message.

--roi 1 instead parses but returns Just (ROI 1 1 1 1)

Is there a way to make this work?


Solution

  • I don't think options are supposed to consume multiple arguments. At least I'm not sure how you'd go about implementing that. I'd suggest simply going away from that idea and putting your ROI options into a single argument, using syntax like --roi 1,2,3,4.

    You'd simply have to implement a custom reader for that, here's an example of how you could do that:

    module Main where
    
    import Options.Applicative
    
    data ROI = ROI Int Int Int Int
      deriving Show
    
    -- didn't remember what this function was called, don't use this
    splitOn :: Eq a => a -> [a] -> [[a]]
    splitOn sep (x:xs) | sep==x     = [] : splitOn sep xs
                       | otherwise = let (xs':xss) = splitOn sep xs in (x:xs'):xss
    splitOn _ [] = [[]]
    
    roiReader :: ReadM ROI
    roiReader = do
      o <- str
      -- no error checking, don't actually do this
      let [a,b,c,d] = map read $ splitOn ',' o
      return $ ROI a b c d
    
    roiParser :: Parser ROI
    roiParser = option roiReader (long "roi")
    
    main :: IO ()
    main = execParser opts >>= print where
      opts = info (helper <*> roiParser) fullDesc