Closed amigalemming closed 6 years ago
Hi,
First up I'm not sure if this sort of validation should be the job of optparse-applicative. I wouldn't know how to express this relationship in a usage summary for instance. Nevertheless, I'll answer as best I can.
Your function definitely inspects the value of a Parser
, so is morally and practically "monadic". You can therefore use the ParserM
type to get most of the way there, but we don't provide a way to emit custom error messages at this level of parser evaluation; i.e., ParserM
doesn't have a valid instance for MonadFail
.
I'll have a think about it, but I don't think this can be done satisfyingly without changing the core Parser
type, which is something I am quite hesitant to do.
I would suggest handling this outside of optparse as a stop gap.
Cheers, Huw
On Fri, 1 Dec 2017, Huw Campbell wrote:
First up I'm not sure if this sort of validation should be the job of optparse-applicative. I wouldn't know how to express this relationship in a usage summary for instance. Nevertheless, I'll answer as best I can.
I understand that the addition of 'join' requires more effort than can be seen from the outside. I don't see the summary as a problem, I would simply ignore 'join' for the summary. Other value constraints are also neither shown in summary nor in the help page. However, a problem might be the error reporting: Currently, execParser says: "option --xxx: this and that problem". If there are multiple options involved it would have to say: "options --xxx and --yyy or --zzz: this and that problem".
My motivation for 'join' is that I want to encapsulate option parsing and return completely checked values such that I can use the options in different contexts. For the size example I can choose between "--width=6 --height=4" and "--size=6x4". The second one allows the check I want, and the first one does not.
For the size example I can choose between "--width=6 --height=4" and "--size=6x4"
The second sounds like the best idea to me, it completely captures the invariant within one ReadM
, and thus allows for the best error reporting. It's also quite similar to imagemagick commands IIRC.
I don't think there's anything really actionable from this issue right now, so I will close. I have however been thinking about more obscure failure cases, so will take this conversation on board in future. It really comes down to finding a good MonadFail
instance for ParserM
(which is the "hidden" monadic interface to optparse).
Ta.
On Sat, 2 Dec 2017, Huw Campbell wrote:
I don't think there's anything really actionable from this issue right now, so I will close. I have however been thinking about more obscure failure cases, so will take this conversation on board in future. It really comes down to finding a good MonadFail instance for ParserM (which is the "hidden" monadic interface to optparse).
I thought a bit about it and think that you can easily get the 'join' and 'fail' feature without touching the current Parser data type. You could define a wrapper:
newtype ParserFail a = ParserFail (Parser (Either String a))
fail :: String -> ParserFail a fail str = ParserFail $ pure $ Left str
join :: ParserFail (Either String a) -> ParserFail a join (ParserFail p) = ParserFail (Monad.join <$> p)
Btw. I think that 'fail' does not help us. We cannot call it within an Applicative context, because that would require (>>=).
i like to generate an error that depends on the values of multiple options. E.g. I have options
--width
and--height
and want to assert that one of the two values is even. A function with this signature would solve the problem for me:Since it is restricted to
Either
it does not turnParser
into a monad. I could then write: