The type of option
is:
option :: ReadM a -> Mod OptionFields a -> Parser a
ReadM
, in turn, is "A newtype over 'ReaderT String Except', used by option readers". Since option
is using ReaderT
under the hood, when you use it with the Applicative
instance of ReadM
like you did here...
ROI <$> auto <*> auto <*> auto <*> auto
... 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 around eitherReader
. 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
).
import Text.Read (readEither)
-- etc.
roi :: Parser (Maybe ROI)
roi = optional
$ option (eitherReader $ \inp -> case traverse readEither (words inp) of
Right [x, y, z, w] -> Right (ROI x y z w)
Right _ -> Left "ROI requires exactly 4 values"
Left _ -> Left "ROI requires integer values")
$ long "roi" <> metavar "ROI" <> help "Only process selected region of interest"
Success and failure modes:
GHCi> execParserPure defaultPrefs (info roi mempty) ["--roi","1 2 3 4"]
Success (Just (ROI 1 2 3 4))
GHCi> execParserPure defaultPrefs (info roi mempty) ["--roi","1 2 3"]
Failure (ParserFailure (option --roi: ROI requires exactly 4 values
Usage: <program> [--roi ROI],ExitFailure 1,80))
GHCi> execParserPure defaultPrefs (info roi mempty) ["--roi","1 2 foo 4"]
Failure (ParserFailure (option --roi: ROI requires integer values
Usage: <program> [--roi ROI],ExitFailure 1,80))