command line flag types are monoids

Duncan Coutts duncan.coutts at worc.ox.ac.uk
Sun Dec 9 16:00:54 EST 2007


warning: rambling ahead...

Perhaps this was obvious to everyone else but I've just come to realise
it over the past couple days as I've been doing more refactoring work on
Cabal and cabal-install's command line handling.

There are basically two kinds of command line flags, ones that represent
a singular piece of information like:

--enable-foo --disable-foo --enable-foo --disable-foo

No matter how many times one uses those kinds of flags, they just set or
reset a single piece of information, in this case a boolean. It doesn't
just have to be a boolean of course:

--user --global --package-db=specific.package.conf

is a flag of type PackageDB.

Then there are command line flags that represent a set or sequence of
information, like:

--ghc-option=this --ghc-option=that --ghc-option=theother

which represents ["this", "that", "theother"]. Lists are of course also
monoids.

So we get the list kind of flag monoid for free and the other is the
Last monoid. Unfortunately it's only in Data.Monoid as of base-3.0, but
it's easy to define a copy:

data Flag a = Flag a | Default

instance Functor Flag where
  fmap f (Flag x) = Flag (f x)
  fmap _ Default  = Default

instance Monoid (Flag a) where
  mempty = Default
  _ `mappend` f@(Flag _) = f
  f `mappend` Default    = f

flagOrDefault default (Flag x) = x
flagOrDefault default Default  = default


So a command usually takes a collection of specific kinds of flags. If
all of these flags are themselves monoids then the overall collection is
also a monoid, defined pointwise:

data ConfigFlags = ConfigFlags {
  foo :: Flag Bool,
  bar :: Flag PackageDB,
  baz :: [String]
}

instance Monoid ConfigFlags where
  mempty = ConfigFlags mempty mempty mempty
  a `mappend` b = ConfigFlags (foo a `mappend` foo b)
                              (bar a `mappend` bar b)
                              (baz a `mappend` baz b)

This is really useful because it means we can combine flags from several
sources, from defaults and override it with command line flags or
setting read from config files. The latter is particularly useful for
cabal-install which has a config file where users can override defaults.

So I'll try converting all the flags types to this Monoid style.

Duncan



More information about the cabal-devel mailing list