[darcs-users] [issue1550] general purpose command line parsing library (CmdArgs)

Eric Kow kowey at darcs.net
Wed Sep 30 20:14:25 UTC 2009

On Fri, Sep 18, 2009 at 16:16:29 +0100, Neil Mitchell wrote:
> The underlying structure is that each command (mode in CmdArgs
> terminology) takes a list of arguments drawn from a larger set of
> arguments. You've optimised for parsing these options by keeping them
> as an unstructured list. CmdArgs optimises for using these arguments
> by having a real structure that can be properly queried.  I would hope
> that you replace your current options code with one using a proper
> structure - since it will make your lives far easier.
> The reason for all this effort is that you've flattened your flags,
> which makes it harder to work with them, and guarantees you have
> subtle bugs all over the place.

Yeah, the lists are definitely a source of potential bugginess.

I wonder: what if we just replaced [DarcsFlag] with Set DarcsFlag.
Would that not achieve the intended effect as well?

Largely irrelevant digression
[Folding: search for "Back to Darcs"]

It doesn't matter to me very much whether we use a record or some sort
of list/set; I was mostly curious.  If I may drop into random
chattiness, in a program called GenI, I also use a list of flags
approach (learning from Darcs).

There are three advantages for me.

1. I can have people write GenI-like applications using the GenI
   library that have a subset of my flags and that add their own.

2. I can then group by flags into thematic sections like "Core options",
   "Input files", and "Optimisations".  When I type geni --help, I
   automatically get the output broken down into sections with some
   flags appearing in more than one section (for example in both 'core
   options' and 'input files').  Darcs actually also does this to an
   extent with its 'Basic' vs 'Advanced' options.

   GenI only has one Mode in CmdArgs terms, but it's a big one!

3. Because I did not want to write getter/setter type functions like
   Darcs has, I have this crazy setup setup involving existential types
   (I think) where I'm passing around a list of Flag

   data Flag = forall f x . (Eq f, Show f, Show x, Typeable f, Typeable x)
                         => Flag (x -> f) x deriving (Typeable)

   Along with functions like

   hasFlag    :: (Typeable f, Typeable x) => (x -> f) -> [Flag] -> Bool
   deleteFlag :: (Typeable f, Typeable x) => (x -> f) -> [Flag] -> [Flag]
   setFlag    :: (Eq f, Show f, Show x, Typeable f, Typeable x) => (x -> f) -> x -> [Flag] -> [Flag]
   getFlag    :: (Show f, Show x, Typeable f, Typeable x)  => (x -> f) -> [Flag] -> Maybe x
Now I'm not entirely satisfied with this approach either because it
means that every time I want to write a flag, I have to

- create a new type
- create a new instance of Flag
- write the help text etc
- export the new type
- import the new type


Here's a little snippet of this in action

#define FLAG(x,y) data x = x y deriving (Eq, Show, Typeable)
FLAG (TestInstructionsFlg, [Instruction])
FLAG (TestSuiteFlg, FilePath)
FLAG (TimeoutFlg, Integer)
FLAG (VerboseModeFlg, ()

Also another disadvantage is that every time I look at this code, I get
the feeling of really going about things in the most complicated
Rube Goldbergesque way I could :-(

Back to Darcs
> It's important to point out that the bits after mode aren't reduced
> implementations - they're completely finished. So for example,
> Amend_Record and Show_Contents both contain match :: Maybe String, but
> only one of their mode implementations needs to describe the flag
> structure, the others inherit it.

Oh! So I remember seeing things like Amend_Record {} which means it
just picks up the values from the last one that defined a field with
the same name/type.  That makes me much less unhappy!

I guess we'd want -fno-warn-missing-fields for the module that has this

> That's a little unfortunate, but not the end of the world - since they
> don't have to describe those flags. It would be possible to do better,
> but not massively better as Haskell records are pretty poor. We could
> do:
> data MatchOne = MatchOne {match :: ..., patch :: ..., index :: ..., tag :: ...}

I think that would be an improvement in terms of making the code easier
to understand.

Eric Kow <http://www.nltg.brighton.ac.uk/home/Eric.Kow>
PGP Key ID: 08AC04F9
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 194 bytes
Desc: not available
URL: <http://lists.osuosl.org/pipermail/darcs-users/attachments/20090930/e0bfa496/attachment-0001.pgp>

More information about the darcs-users mailing list