[Haskell-cafe] Handling platform- or configuration-specific code (or, my CPP complaints)

Ben Millwood haskell at benmachine.co.uk
Tue Sep 7 15:10:17 EDT 2010

Good evening, cafe,

Having recently taken on maintenance of a package that depends on
template-haskell, I've been in some discussion with users and
dependencies of my package about how best to write a library that
works with multiple incompatible versions of a dependency. The two
main approaches that I'm aware of are:
1. Use CPP and the macros defined by Cabal to conditionally include or
exclude code for each version.
2. Use Cabal file conditionals to select hs-source-dirs containing
those parts of the code (or even TH to help generate those parts of
the code) that are specific to each configuration.

In my discussion with others and examination of existing libraries, it
seems to me that 1. is the preferred option, but I personally can
think of a number of reasons to prefer the second option:
* CPP was not designed for Haskell, and even cpphs still shows
symptoms of it, so we have to worry about things like its handling of
string gaps or single quotes.
* CPP is primarily a textual manipulation tool, rather than a symbolic
one, which makes it even more easy to produce malformed code with it
than with TH or similar.
* On that note, it's difficult to statically analyse a file using CPP:
parser tools like haskell-src-exts don't support it and it's not at
all obvious how or if they ever could. With Cabal choosing source
files based on configuration, all the source is valid, normal Haskell
and is easy for computers and humans to understand.
* It may require some significant digging into each source file to
establish what configurations must be tested, or to add a new
supported configuration or remove an old one, if they are chosen based
on CPP conditional compilation. When Cabal chooses what is compiled it
is fairly explicit what is chosen and what could be, and adding a new
configuration is - at least in theory - as simple as adding a new file
and conditional.
* It's just not very pretty! Haskell code has a sort of aesthetic that
I don't think CPP macros share - different function application
syntax, for example.

Of course the trouble is that when your conditional compilation is on
the module level there are some things which just can't be done
without code duplication, and there's a tendency for small and often
quite incidental bits of a function definition to suddenly be in
another module instead of where they're used. So I wonder what people
think of the use of CPP in Haskell code, what alternatives people can
propose, or what people hope to see in future to make conditional
compilation of Haskell code more elegant and simple?

I once pondered whether it would be a good idea to somehow make
available to TH splices information from Cabal or GHC's configuration,
so that one could do something like this:

myFunctionDecl = if packageVersion "template-haskell" >= Version [2, 4] []
  then [d| unTyVarBndr (PlainTV n) = n; unTyVarBndr (KindedTV n _) = n |]
  else [d| unTyVarBndr = id |]

This exactly wouldn't make sense because KindedTV wouldn't exist in
the earlier version so the quotation would object, but one could
imagine something similar being useful.

More information about the Haskell-Cafe mailing list