
Dear List, After recent discussions[1][2], from which I gathered that (compile-time) conditional APIs should be avoided, I'm left wondering if a certain kind of compile-time conditional API falls into that category as well, or whether it can be classified as "benign" and should therefore be tolerated. So I'm posting this for open discussion in the hope to reach a community consensus on whether the conditional use of the "DefaultSignature" language extension in APIs should be tolerated or not (e.g. for haskell-platform packages). To this end, in the rest of this post I try to make a case for why this class of conditional APIs is harmless. ----- So what's bad about "conditional" APIs? The way I understand the issue with respect to compile-time conditional APIs which either a) omit exposed modules or exported symbols altogether based on compile-time configuration, or even worse b) export different type-signatures for the very same symbol based on compile-time configuration are "bad", as they cause problems in client code using those APIs, such as a) not being able to properly declare the dependency on specific API features being enabled in the library API in CABAL files (since CABAL only allows to depend package version numbers), b) and from the other direction, there's no simple way for the library package to advertise the conditional features currently present in the registered package. c) Moreover, if the client code wants to use symbols which depend on compile-time configuration, it either becomes non-portable and/or becomes compile-time conditional itself. Note: It should be stressed that in the latter case the client code needs to take care to *exactly* replicate the semantics of the conditional switching between the API variants as employed by the library code (which can be CPP-based and/or CABAL-based), as any mismatch may result in compile time failures. This also poses the issue on how to properly document this "meta-property" in the library API documentation. (there may be more reasons, I just listed the ones off the top of my head) On the other hand, I think that there are cases, when a compile-time conditional API is "benign", for instance when the following condition holds: a) the library package exposes a /non-essential/ API feature based only on availability of a given compiler-feature/extension, b) the conditional feature is only ever visible to the client code if the client makes use of this compiler-feature itself, and last but not least c) the semantics of the client code must not change if the client code has been made portable (with respect to the availability of the conditional feature) E.g. consider the following hypothetical class definition: class Size a where size :: a -> Int #ifdef HAS_DEFAULT_SIGNATURE default size :: Generic a => a -> Int size x = ...Generics based implementation... #endif Now, if the client code doesn't exploit the default-signature implementation, then the client code is perfectly portable, and even if the conditional API features are available they have no visible effect whatsoever. OTOH, if the client code wants to make use of the default implementation, it needs to be built with a compiler supporting the Generics language feature, and due to a) the library is guaranteed to provide the default-signature implementation as well as soon as the client is able to use it. The client-code is now deliberately non-portable (but on the other hand, the operational semantics are guaranteed to be consistent, as there's only one case to consider) Consequently, as both scenarios don't lead to problematic situations, I'd argue that there is no harm in allowing this kind of compile-time conditional APIs using the DefaultSignature language extension. ---- cheers, hvr [1]: "Proposal: Adding generics-based rnf-helper to `deepseq`" on libraries list http://comments.gmane.org/gmane.comp.lang.haskell.libraries/17940 [2]: "[ANNOUNCE] hashable-generics" on haskell-cafe list http://comments.gmane.org/gmane.comp.lang.haskell.cafe/101323