haskelloptparse-applicative

How to parse a product type partially


data Config = Config {
    a :: Bool,
    b :: Type1,
    c :: Type2
}

pA :: Parser Bool

pB :: Parser Type1

pC :: Parser Type2

pConfig :: Parser Config
pConfig = Config <$> pA <*> pB <*> pC

opts :: ParserInfo Config
opts = info (pConfig <**> helper)
       (fullDesc <> progDesc "My CLI" <> header "CLI executable")

main :: IO()
main = do
       (Config a b c) <- execParser opts
-- Populate a default config using a b c values

Is it possible to parse a product type partially? Config is a product type with member a, b and c and assume this comes from a library, so I cannot redefine this. I only want to parse a and b without caring about c. But, since a "Parser Config" can only have a construction like below

Config <$> pA <*> pB <*> pC

due to being a product type, if I do not give a "pC" it errors out. How to correctly handle this scenario?


Solution

  • The Config <$> pA <*> pB <*> pC notation doesn’t care that Config is a constructor; you could have used any function of type Bool -> Type1 -> Type2 -> Config. If you don’t want to parse a Type2, you could use any function of type Bool -> Type1 -> Config.

    config' :: Bool -> Type1 -> Config
    config' a b c = Config a b someDefaultType2
    
    pConfig :: Parser Config
    pConfig = config' <$> pA <*> pB
    

    Equivalently, you stick with the Config constructor but connect a pure value instead of a parser to its third argument.

    pConfig :: Parser Config
    pConfig = Config <$> pA <*> pB <*> pure someDefaultType2
    

    (Either way, you do need to have some way of providing a value of Type2, if you want to get a Config out.)