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. _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe