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?
The type of
option
is:ReadM
, in turn, is "A newtype over 'ReaderT String Except', used by option readers". Sinceoption
is usingReaderT
under the hood, when you use it with theApplicative
instance ofReadM
like you did here...... the same, and whole, input string is supplied to each of the four
auto
parsers, because that's how the reader/function applicative instances work.If you want values separated by spaces to be parsed into a single
ROI
, you need to write a custom parser. Here is a not particularly tidy attempt at that, built aroundeitherReader
. Note that this will require the values to be within quotation marks (--roi "1 2 3 4"
), so that they are taken in as a single string. Cubic's answer suggests an alternative approach, which uses comma-separated values instead (--roi 1,2,3,4
).Success and failure modes:
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: