th-compat 0.1.1 → 0.1.2
raw patch · 4 files changed
+187/−20 lines, 4 files
Files
- CHANGELOG.md +5/−0
- src/Language/Haskell/TH/Syntax/Compat.hs +165/−17
- tests/Language/Haskell/TH/Syntax/CompatSpec.hs +13/−0
- th-compat.cabal +4/−3
CHANGELOG.md view
@@ -1,3 +1,8 @@+### 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`.
src/Language/Haskell/TH/Syntax/Compat.hs view
@@ -57,7 +57,7 @@ -- ** The @IsCode@ class , IsCode(..) -- ** Limitations of @IsCode@- -- $limitations+ -- $isCodeLimitations -- ** Functions from @Language.Haskell.TH.Syntax@ , unsafeCodeCoerce , liftCode@@ -70,8 +70,15 @@ -- * @Splice@ , Splice , SpliceQ+ , bindSplice+ , bindSplice_+ , examineSplice+ , hoistSplice+ , joinSplice+ , liftSplice , liftTypedFromUntypedSplice , unsafeSpliceCoerce+ , unTypeSplice #endif ) where @@ -450,9 +457,21 @@ -- One troublesome aspect of writing backwards-compatible code involving 'Code' -- is that GHC 9.0 changed the types of typed Template Haskell splices. Before, -- they were of type @'Q' ('TExp' a)@, but they are now of type @'Code' 'Q' a@.--- The 'IsCode' class can be used to paper over the difference between these--- two types. For more details, consult the Haddocks for 'IsCode'.+-- This modules provides two mechanisms for smoothing over the differences+-- between these two types: --+-- * The 'IsCode' class can be used to convert 'Code' or 'TExp' values to+-- 'Code', and vice versa.+--+-- * The 'Splice' type synonym uses CPP so that @'Splice' q a@ is a synonym for+-- @'Code' q a@ on GHC 9.0 or later and @q ('TExp' a)@ on older versions of+-- GHC. This module also defines versions of 'Code'- and 'TExp'-related+-- combinators that work over 'Splice'.+--+-- Refer to the Haddocks for 'IsCode' and 'Splice' for more information on each+-- approach. Both approaches have pros and cons, and as a result, neither+-- approach is a one-size-fits-all solution.+-- -- Because 'Code' interacts with typed Template Haskell, the 'Code' type and -- any function that mentions 'Code' in its type are only defined on -- @template-haskell-2.9.0.0@ (GHC 7.8) or later.@@ -517,7 +536,7 @@ toCode = liftCode fromCode = examineCode --- $limitations+-- $isCodeLimitations -- 'IsCode' makes it possible to backport code involving typed Template Haskell -- quotations and splices where the types are monomorphized to 'Q'. GHC 9.0 -- and later, however, make it possible to use typed TH quotations and splices@@ -675,8 +694,7 @@ -- which version of @template-haskell@ you are using. It is mostly useful for -- contexts in which one is writing a definition that is intended to be used -- directly in a typed Template Haskell splice, as the types of TH splices--- differ between @template-haskell@ versions as well. One example of a type--- that uses 'Splice' is the type signature for 'lifTypedFromUntypedSplice'.+-- differ between @template-haskell@ versions as well. -- -- Levity-polymorphic since /template-haskell-2.16.0.0/. # if MIN_VERSION_template_haskell(2,17,0)@@ -708,6 +726,121 @@ type SpliceQ a = Splice Q a # endif +-- | A variant of 'bindCode' that works over 'Splice's. Because this function+-- uses 'Splice', the type of this function will be different depending on+-- which version of @template-haskell@ you are using. (See the Haddocks for+-- 'Splice' for more information on this point.)+--+-- Levity-polymorphic since /template-haskell-2.16.0.0/.+bindSplice ::+# if MIN_VERSION_template_haskell(2,16,0)+ forall m a (r :: RuntimeRep) (b :: TYPE r) .+# else+ forall m a b .+# endif+ Monad m => m a -> (a -> Splice m b) -> Splice m b+# if MIN_VERSION_template_haskell(2,17,0)+bindSplice = bindCode+# else+bindSplice q k = liftSplice (q >>= examineSplice . k)+# endif++-- | A variant of 'bindCode_' that works over 'Splice's. Because this function+-- uses 'Splice', the type of this function will be different depending on+-- which version of @template-haskell@ you are using. (See the Haddocks for+-- 'Splice' for more information on this point.)+--+-- Levity-polymorphic since /template-haskell-2.16.0.0/.+bindSplice_ ::+# if MIN_VERSION_template_haskell(2,16,0)+ forall m a (r :: RuntimeRep) (b :: TYPE r) .+# else+ forall m a b .+# endif+ Monad m => m a -> Splice m b -> Splice m b+# if MIN_VERSION_template_haskell(2,17,0)+bindSplice_ = bindCode_+# else+bindSplice_ q c = liftSplice ( q >> examineSplice c)+# endif++-- | A variant of 'examineCode' that takes a 'Splice' as an argument. Because+-- this function takes a 'Splice' as an argyment, the type of this function+-- will be different depending on which version of @template-haskell@ you are+-- using. (See the Haddocks for 'Splice' for more information on this point.)+--+-- Levity-polymorphic since /template-haskell-2.16.0.0/.+examineSplice ::+# if MIN_VERSION_template_haskell(2,16,0)+ forall (r :: RuntimeRep) m (a :: TYPE r) .+# else+ forall m a .+# endif+ Splice m a -> m (Syntax.TExp a)+# if MIN_VERSION_template_haskell(2,17,0)+examineSplice = examineCode+# else+examineSplice = id+# endif++-- | A variant of 'hoistCode' that works over 'Splice's. Because this function+-- uses 'Splice', the type of this function will be different depending on+-- which version of @template-haskell@ you are using. (See the Haddocks for+-- 'Splice' for more information on this point.)+--+-- Levity-polymorphic since /template-haskell-2.16.0.0/.+hoistSplice ::+# if MIN_VERSION_template_haskell(2,16,0)+ forall m n (r :: RuntimeRep) (a :: TYPE r) .+# else+ forall m n a .+# endif+ Monad m => (forall x . m x -> n x) -> Splice m a -> Splice n a+# if MIN_VERSION_template_haskell(2,17,0)+hoistSplice = hoistCode+# else+hoistSplice f a = f a+# endif++-- | A variant of 'joinCode' that works over 'Splice's. Because this function+-- uses 'Splice', the type of this function will be different depending on+-- which version of @template-haskell@ you are using. (See the Haddocks for+-- 'Splice' for more information on this point.)+--+-- Levity-polymorphic since /template-haskell-2.16.0.0/.+joinSplice ::+# if MIN_VERSION_template_haskell(2,16,0)+ forall m (r :: RuntimeRep) (a :: TYPE r) .+# else+ forall m a .+# endif+ Monad m => m (Splice m a) -> Splice m a+# if MIN_VERSION_template_haskell(2,17,0)+joinSplice = joinCode+# else+joinSplice = flip bindSplice id+# endif++-- | A variant of 'liftCode' that returns a 'Splice'. Because this function+-- returns a 'Splice', the return type of this function will be different+-- depending on which version of @template-haskell@ you are using. (See the+-- Haddocks for 'Splice' for more+-- information on this point.)+--+-- Levity-polymorphic since /template-haskell-2.16.0.0/.+liftSplice ::+# if MIN_VERSION_template_haskell(2,16,0)+ forall (r :: RuntimeRep) (a :: TYPE r) m .+# else+ forall a m .+# endif+ m (Syntax.TExp a) -> Splice m a+# if MIN_VERSION_template_haskell(2,17,0)+liftSplice = liftCode+# else+liftSplice = id+# endif+ -- | A variant of 'liftTypedQuote' that is: -- -- 1. Always implemented in terms of 'Syntax.lift' behind the scenes, and@@ -717,7 +850,7 @@ -- using. (See the Haddocks for 'Splice' for more information on this -- point.) ----- This is primarily useful for minimizing CPP in one particular scenario:+-- This is especially useful for minimizing CPP in one particular scenario: -- implementing 'Syntax.liftTyped' in hand-written 'Syntax.Lift' instances -- where the corresponding 'Syntax.lift' implementation cannot be derived. For -- instance, consider this example from the @text@ library:@@ -755,17 +888,13 @@ liftTypedFromUntypedSplice :: (Syntax.Lift t, Quote m) => t -> Splice m t liftTypedFromUntypedSplice = unsafeSpliceCoerce . liftQuote --- | Unsafely convert an untyped code representation into a typed code--- representation, where:------ * The splice representation is @'Code' m a@, if using--- @template-haskell-2.17.0.0@ or later, or------ * The splice representation is @m ('Syntax.TExp' a)@, if using an older--- version of @template-haskell@.+-- | Unsafely convert an untyped splice representation into a typed 'Splice'+-- representation. Because this function returns a 'Splice', the return type of+-- this function will be different depending on which version of+-- @template-haskell@ you are using. (See the Haddocks for 'Splice' for more+-- information on this point.) ----- This is primarily useful for minimizing CPP when the following two--- conditions are met:+-- This is especially useful for minimizing CPP when: -- -- 1. You need to implement 'Syntax.liftTyped' in a hand-written 'Syntax.Lift' -- instance where the corresponding 'Syntax.lift' implementation cannot be@@ -801,5 +930,24 @@ unsafeSpliceCoerce = unsafeCodeCoerce # else unsafeSpliceCoerce = unsafeTExpCoerceQuote+# endif++-- | A variant of 'unTypeCode' that takes a 'Splice' as an argument. Because+-- this function takes a 'Splice' as an argyment, the type of this function+-- will be different depending on which version of @template-haskell@ you are+-- using. (See the Haddocks for 'Splice' for more information on this point.)+--+-- Levity-polymorphic since /template-haskell-2.16.0.0/.+unTypeSplice ::+# if MIN_VERSION_template_haskell(2,16,0)+ forall (r :: RuntimeRep) (a :: TYPE r) m .+# else+ forall a m .+# endif+ Quote m => Splice m a -> m Exp+# if MIN_VERSION_template_haskell(2,17,0)+unTypeSplice = unTypeCode+# else+unTypeSplice = unTypeQQuote # endif #endif
tests/Language/Haskell/TH/Syntax/CompatSpec.hs view
@@ -43,9 +43,22 @@ it "manipulates typed TH expressions in a backwards-compatible way" $ $$(fromCode (toCode [|| "abc" ||])) `shouldBe` "abc" + describe "joinSplice" $+ it "allows intermixing typed TH splices with monadic computations in a convenient way" $+ $$(joinSplice (do { x <- return "abc"; return [|| x ||] })) `shouldBe` "abc"++ describe "liftSplice" $+ it "allows intermixing typed TH splices with monadic computations in a convenient way" $+ $$(liftSplice (do { x <- return "abc"; examineSplice [|| x ||] })) `shouldBe` "abc"+ describe "liftTypedFromUntypedSplice" $ it "allows defining liftTyped in a convenient, backwards-compatible way" $ $$(liftTypedFromUntypedSplice MkFoo) `shouldBe` MkFoo++ describe "unTypeSplice" $+ it "allows unwrapping Code in a convenient, backwards-compatible way" $+ $$(unsafeSpliceCoerce (return . ListE =<< traverse unTypeSplice [ [|| "abc" ||] ]) :: SpliceQ [String])+ `shouldBe` ["abc"] #endif newtype PureQ a = MkPureQ (State Uniq a)
th-compat.cabal view
@@ -1,6 +1,6 @@ cabal-version: >=1.10 name: th-compat-version: 0.1.1+version: 0.1.2 synopsis: Backward- (and forward-)compatible Quote and Code types description: This package defines a "Language.Haskell.TH.Syntax.Compat" module, which backports the @Quote@ and @Code@ types to@@ -29,8 +29,9 @@ , GHC == 8.2.2 , GHC == 8.4.4 , GHC == 8.6.5- , GHC == 8.8.3- , GHC == 8.10.1+ , GHC == 8.8.4+ , GHC == 8.10.4+ , GHC == 9.0.1 extra-source-files: CHANGELOG.md, README.md source-repository head