Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to code for mutually exclusive flags in Options.Applicative

I'm a Haskell newbie. As a learning exercise, I'm trying to port one of my Rust programs to Haskell. In Rust, I use the amazing clap package, and have come across Options.Applicative as a good-looking alternative. Here's an example:

import Options.Applicative
import Data.Semigroup ((<>))

data Sample = Sample
  { tod        :: Bool
  , pmc        :: Bool
  , tai        :: Bool
  }

sample :: Parser Sample
sample = Sample
      <$>  switch
          ( long "tod"
            <> short 'o'
            <> help "Convert from TOD" )
      <*> switch
          ( long "pmc"
            <> short 'p'
            <> help "Convert from PMC" ) 
      <*> switch
          ( long "tai"
            <> short 't'
            <> help "Set TAI mode" )

main :: IO ()
main = greet =<< execParser opts
  where
    opts = info (sample <**> helper) ( fullDesc )
greet :: Sample -> IO ()
greet (Sample a b c) = print [a,b,c]

Having got this far, I've hit a brick wall. I need to make the "tod" and "pmc" flags mutually exclusive. There's an example in the package README which uses <|>, but it's not for boolean flags, and I don't have a clue how to transform this.

Can anyone help, please?

like image 255
Brent.Longborough Avatar asked Sep 04 '25 16:09

Brent.Longborough


1 Answers

Make one of pmc or tod be a computed value, and only store the other.

data Sample = Sample
    { tod :: Bool
    , tai :: Bool
    }

pmc = not . tod

sample = Sample
    <$> (   flag' True  (short 'o')
        <|> flag' False (short 'p')
        <|> pure True -- what do you want to do if they specify neither?
        )
    <*> switch (short 't')

Or perhaps there are actually three modes of operation. Then make both tod and pmc be computed fields.

data Mode = TOD | PMC | Other deriving Eq

data Sample = Sample
    { mode :: Mode
    , tai :: Bool
    }

tod = (TOD==) . mode
pmc = (PMC==) . mode

sample = Sample
    <$> (   flag' TOD (short 'o')
        <|> flag' PMC (short 'p')
        <|> pure Other
        )
    <*> switch (short 't')
like image 189
Daniel Wagner Avatar answered Sep 07 '25 17:09

Daniel Wagner