By Colin on 2020-03-05, updated 2020-05-05

The Hackage Matrix Builder is a great way to track how compatible a library is with historical versions of GHC (and therefore base ).

As a library author, if you've ever attempted to support more than the most recent versions of GHC, then you've been tossed around the ocean of Applicative , Semigroup , and MonadFail , with CPP as your only olive branch.

This post is a reference for library authors to easily track how base evolves, and how to deal with its changes. This is so that we don't all have to repeat the same labour of digging through GHC release notes or the base CHANGELOG to find out what works. I will update this post as time goes on, so please feel free to bookmark it or even help update it.

Compatibility Chart

GHC base version Stackage LTS Cabal Notable Changes 8.10 4.14 +RTS -xn for non-moving, concurrent GC 8.8 4.13 16 3.0 MonadFail exported by Prelude fail removed from Monad 8.6 4.12 14 2.4 -funclutter-valid-hole-fits available deriving via available 8.4 4.11 12 2.2 Semigroup is superclass of Monoid Semigroup exported by Prelude 8.2 4.10 11 2.0 DerivingStrategies available 8.0 4.9 9 1.24 MonadFail enters base Semigroup enters base 7.10 4.8 6 1.22 Applicative is superclass of Monad Applicative exported by Prelude 7.8 4.7 2 1.18

Compatibility Pearls

Intent: For base earlier than 4.n , do X {-# LANGUAGE CPP #-} # if ! MIN_VERSION_base ( 4 ,n, 0 ) if,n, import FooBar # endif endif A construction like #if __GLASGOW_HASKELL__ < 841 is also possible, but I find this less clear than comparing against base . Backwards-compatible Monoid Instances This will allow your library to support GHC >= 8.0. Excluding this CPP will limit your compatibility to GHC >= 8.4. {-# LANGUAGE CPP #-} # if ! MIN_VERSION_base ( 4 , 11 , 0 ) if import Data.Semigroup # endif endif instance Semigroup Foo where a <> b = ... instance Monoid Foo where mempty = ... # if ! MIN_VERSION_base ( 4 , 11 , 0 ) if mappend = ( <> ) # endif endif Optional dependencies based on GHC version For instance, until GHC 8.0, the Semigroup typeclass was not in base . The following will include the semigroups dependency, but only when it detects that you need it: library build-depends: base >= 4.8 && < 5 ... if !impl(ghc >= 8.0) build-depends: semigroups >= 0.8.4 && < 1 Multiple build-depends sections like this combine, they don't override one another. *-compat packages There are a number of packages that smooth the transition between compiler versions by backporting newer functionality. The major ones are: base-compat, which backports new functions to old GHCs

base-orphans, its sister package

base-compat-batteries, similar to base-compat but with more dependencies

but with more dependencies prelude-compat, which helps massage the import confusion surrounding SMP, AMP and FTP To see all such libraries, search for "compat" on Hackage. Misc. Ecosystem Pearls Minimal dependencies for these As of these-1 the project underwent a structural change. Its extended dependency graph is now "opt out", and the following can be added to a stack.yaml to accomplish this: flags : these : aeson : false assoc : false semigroupoids : false QuickCheck : false Minimal dependencies for witherable wither :: Applicative f => (a -> f (Maybe b)) -> t a -> f (t b) is a convenient function from the witherable package. As of 2019 December, the core typeclass and functions are available with minimal dependencies via the witherable-class library. Your library's version in your code This trick lets your library/program have programmatic access to the version: value you specified in your .cabal file. {-# LANGUAGE CPP #-} # ifndef CURRENT_PACKAGE_VERSION ifndef # define CURRENT_PACKAGE_VERSION "UNKNOWN" define # endif endif ver :: Text = CURRENT_PACKAGE_VERSION ver

Resources