-
Notifications
You must be signed in to change notification settings - Fork 7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Doesn't work as expected with optional #23
Comments
Just wanted to let anyone interested know that there is a workaround... Instead of something like, optional (Env.var f "NAME" g) You can do Env.var (Just <$> f) "NAME" (g <> Env.def Nothing) In my testing so far, it behaves how I want:
|
While I'm sure there's a reason {-# LANGUAGE LambdaCase #-}
module Main where
import Control.Applicative ((<|>))
import Data.Bifunctor (first)
import qualified Env
import qualified Options.Applicative as Options
main :: IO ()
main = do
e <- Env.parse (Env.header "example") envParser
putStrLn $ "Via ENV: " <> show e
o <- Options.execParser $ Options.info optParser Options.fullDesc
putStrLn $ "Via CLI: " <> show o
data Thing = Thing
deriving Show
readThing :: String -> Either String Thing
readThing = \case
"thing" -> Right Thing
x -> Left $ "Must be thing, was " <> x
conjureThing :: Applicative m => m Thing
conjureThing =
pure Thing
envParser :: Env.Parser Env.Error Thing
envParser =
Env.var (first Env.UnreadError . readThing) "THING" mempty
<|>
conjureThing
optParser :: Options.Parser Thing
optParser =
Options.option (Options.eitherReader readThing) (Options.long "thing")
<|>
conjureThing
|
I like your workaround in that it clearly separates the case of a failing parser and an entirely missing variable. Maybe this should be provided by the library. |
Yeah, I can definitely see the point that, for any monad with failure semantics If some CLI tool wants If you squint, it could be justified if you think of |
I keep bumping into this, and having to remember, explain to my team, and code in the workaround I supplied is getting pretty tedious (and error-prone). I'm curious if we can discuss a bit more about changing
This implies to me that whatever reason I think the reason |
An expression like `a <|> b :: Parser _` only falls back to `b` if `a` fails in the "required option missing" sense, and not (for example) in the "`eitherReader` returned `Left`" sense. That, as far as I can tell, makes it impossible to do the parser so the it comes out as `[ LocalPath S3Uri | ... ]` as we'd like, since I can't make a two-argument local-remote parser "fail" and fall-back into remote-local, and finally remote-remote, and never attempt to parse local-local. Instead, I need to parse `[ LocalPath | S3Uri ]` twice, and even then I have to put the "or" semantics into the function given to `eitherReader` to avoid the same issue, which leaves `--help` not-ideal still, but at least function. And since this style has no way to avoid parsing local-local as valid, I need to handle that outside, which means `error` (or not using a `Parser SyncOptions`, which is annoying). Sigh. It's possible `optparse-applicative` is "wrong" here. In my issue `envparse`[^1], I'm actually calling how `envparse` behaves a bug citing `optparse-applicative` as the "right" way. But now I'm having second thoughts. [^1]: supki/envparse#23
An expression like `a <|> b :: Parser _` only falls back to `b` if `a` fails in the "required option missing" sense, and not (for example) in the "`eitherReader` returned `Left`" sense. That, as far as I can tell, makes it impossible to do the parser so the it comes out as `[ LocalPath S3Uri | ... ]` as we'd like, since I can't make a two-argument local-remote parser "fail" and fall-back into remote-local, and finally remote-remote, and never attempt to parse local-local. Instead, I need to parse `[ LocalPath | S3Uri ]` twice, and even then I have to put the "or" semantics into the function given to `eitherReader` to avoid the same issue, which leaves `--help` not-ideal still, but at least function. And since this style has no way to avoid parsing local-local as valid, I need to handle that outside, which means `error` (or not using a `Parser SyncOptions`, which is annoying). Sigh. It's possible `optparse-applicative` is "wrong" here. In my issue `envparse`[^1], I'm actually calling how `envparse` behaves a bug citing `optparse-applicative` as the "right" way. But now I'm having second thoughts. [^1]: supki/envparse#23
I'm willing to bet that I've got a misunderstanding here, but I'm surprised by the behavior of
Env.Parser
withoptional
:The following is something I do quite a bit with
optparse-applicative
:The option is itself built with
eitherReader
, so invalid input (viaLeft
) is an error. But then it's wrapped withoptional
to make it, well, optional. This is a great separation of concerns as I'll often define the option (Parser Thing
) in one place, and then wrap it up withoptional
(or not) in another place (Parser Things
).Importantly, this does not replace errors with
Nothing
. If you run this program with missing input, you getNothing
. But if you run it with invalid input you get an error,This is what I want.
I'd expect the equivalent
envparse
program, to behave the same in this regard:But it does not. You just get
Nothing
for both missing and invalid input.Is this expected?
Complete example program
The text was updated successfully, but these errors were encountered: