Backward- (and forward-)compatible Quote and Code types

Version on this page:0.1.3
LTS Haskell 19.7:0.1.3@rev:1
Stackage Nightly 2022-05-20:0.1.3@rev:1
Latest on Hackage:0.1.3@rev:1

See all snapshots th-compat appears in

BSD-3-Clause licensed by Ryan Scott
Maintained by Ryan Scott
This version can be pinned in stack with:th-compat-0.1.3@sha256:a16dd922947a6877defe52c4c38d1ab48ed3f85a826930f5d1a568741d619993,2888

Module documentation for 0.1.3


Hackage Hackage Dependencies Haskell Programming Language BSD3 License Build Status

This package defines a Language.Haskell.TH.Syntax.Compat module, which backports the Quote and Code types to work across a wide range of template-haskell versions. On recent versions of template-haskell ( or later), this module simply reexports Quote and Code from Language.Haskell.TH.Syntax. Refer to the Haddocks for Language.Haskell.TH.Syntax.Compat for examples of how to use this module.

Quick Start Guide

Let’s say you have a library that offers a foo :: Q (TExp a), you want to make it compatible with the new Code type, and you intend that foo is spliced directly in to user code.

Use SpliceQ as a type alias for the return of your function. This is Q (TExp a) prior to GHC 9, and Code Q a after. This allows your code to be spliced in regardless of GHC version.

Use liftSplice to convert a m (TExp a) into a Splice m a.

Use examineSplice before typed quoters. This will allow a typed quasiquotation to work regardless of GHC version.

When splicing in a TExp a value into a typed quoter, use expToSplice.

For a real life example, consider this conversion, from this PR:

    :: forall c. (Typeable c)
    => Q (TExp [SomeDict c])
discoverInstances = do
    let className = show (typeRep (Proxy @c))
    instanceDecs <- reifyInstances (mkName className) [VarT (mkName "a")]

    dicts <- fmap listTE $ traverse decToDict instanceDecs

    [|| concat $$(pure dicts) ||]

listTE :: [TExp a] -> TExp [a]
listTE = TExp . ListE . map unType

decToDict :: InstanceDec -> Q (TExp [SomeDict c])

With GHC 9, this will have the following problems:

  1. reifyInstances operates in Q, not Code, so it will not type check with the [|| concat $$(pure dicts) ||] line.
  2. We cannot call pure in Code, since Code is not an applicative.
  3. Typed quasiquotes return a Quote m => Code m a, not Q (TExp a).

To fix these problems, we make the following diff:

     :: forall c. (Typeable c)
-    => Q (TExp [SomeDict c])
+    => SpliceQ [SomeDict c]
- discoverInstances = do
+ discoverInstances = liftSplice $ do
     let className = show (typeRep (Proxy @c))
     instanceDecs <- reifyInstances (mkName className) [VarT (mkName "a")]

     dicts <- fmap listTE $ traverse decToDict instanceDecs

-     [|| concat $$(pure dicts) ||]
+     examineSplice [|| concat $$(expToSplice dicts) ||]

The above pattern should work to ensure that code is compatible across a wide range of GHC versions.


0.1.3 [2021.08.29]

  • Implement qGetDoc and qPutDoc in the Quasi instance for QuoteToQuasi.
  • Add expToSplice.

0.1.2 [2021.03.12]

  • Add bindSplice, bindSplice_, examineSplice, joinSplice, hoistSplice, liftSplice, and unTypeSplice to Language.Haskell.TH.Syntax.Compat.

0.1.1 [2021.02.07]

  • Mark Language.Haskell.TH.Syntax.Compat as Trustworthy.

0.1 [2020.09.29]

  • Initial release