th-compat: Backward- (and forward-)compatible Quote and Code types

[ bsd3, library, text ] [ Propose Tags ]

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. The makeRelativeToProject utility is also backported. On recent versions of template-haskell (2.17.0.0 or later), this module simply reexports definitions from Language.Haskell.TH.Syntax. Refer to the Haddocks for Language.Haskell.TH.Syntax.Compat for examples of how to use this module.


[Skip to Readme]

Modules

[Index] [Quick Jump]

Downloads

Note: This package has metadata revisions in the cabal description newer than included in the tarball. To unpack the package including the revisions, use 'cabal get'.

Maintainer's Corner

Package maintainers

For package maintainers and hackage trustees

Candidates

  • No Candidates
Versions [RSS] 0.1, 0.1.1, 0.1.2, 0.1.3, 0.1.4, 0.1.5
Change log CHANGELOG.md
Dependencies base (>=4.3 && <5), directory (>=1.1.0.0 && <1.4), fail (>=4.9 && <4.10), filepath (>=1.2.0.0 && <1.6), template-haskell (>=2.5 && <2.23), transformers (>=0.2 && <0.7) [details]
License BSD-3-Clause
Copyright (C) 2020 Ryan Scott
Author Ryan Scott
Maintainer Ryan Scott <ryan.gl.scott@gmail.com>
Revised Revision 2 made by ryanglscott at 2024-04-30T15:47:24Z
Category Text
Home page https://github.com/haskell-compat/th-compat
Bug tracker https://github.com/haskell-compat/th-compat/issues
Source repo head: git clone https://github.com/haskell-compat/th-compat
Uploaded by ryanglscott at 2024-03-19T11:25:56Z
Distributions Arch:0.1.5, Fedora:0.1.4, LTSHaskell:0.1.5, NixOS:0.1.5, Stackage:0.1.5, openSUSE:0.1.5
Reverse Dependencies 20 direct, 8264 indirect [details]
Downloads 30504 total (415 in the last 30 days)
Rating (no votes yet) [estimated by Bayesian average]
Your Rating
  • λ
  • λ
  • λ
Status Docs available [build log]
Last success reported on 2024-03-19 [all 1 reports]

Readme for th-compat-0.1.5

[back to package description]

th-compat

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 (2.17.0.0 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:

discoverInstances
    :: 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:

 discoverInstances
     :: 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.