Reputation: 3747
I have function (let's call it mkSome
) which constructs some data type with Template Haskell. It has typical signature Name -> Q [Dec]
.
Somewhere in its body I'm extracting constructors of another type with
pattern-matching:
case tyCons of
DataD ctx nm tyVars mbKind cs derivs -> ...
Type of those constructors cs
instantiates some class like this:
class MyClass a where
specialValue :: a
So, I'm iterating over those cs
but I want to skip one of them which is
equal to specialValue
. Something like this:
[c | c <- cs, c /= specialValue]
Example:
data OtherData = A | B | C
instance MyClass OtherData where
specialValue = C
$(mkSome ''OtherData) -- mkSome must skip C-constructor!
How to do this in Template Haskell's (with Con
type: c
is it) ? Sure, I can't simply call constructor to compare created value with a specialValue
because it's AST node, not real constructor
Upvotes: 1
Views: 330
Reputation: 14588
It depends entirely on how you want to use this expression. You can write e.g.
mkCons :: Name -> Q Exp
mkCons ty = do
TyConI (DataD ctx nm tyVars mbKind cs derivs) <- reify ty
let cons = ListE $ map (\(NormalC c _) -> ConE c) cs
[| [c | c <- $(pure cons), c /= specialValue] |]
which is a splice whose result is the constructors of ty
except specialValue
.
But if you want to manipulate the resulting list within the splice (e.g. generate some code for all constructors except specialValue
) then the situation is much more complicated. You'll need to have a nested splice which manipulates the result of the above splice:
mkSome :: Name -> Q Exp
mkSome ty =
[| do e1 <- mapM lift $(mkCons ty)
let mkD (ConE n) = DataD [] (mkName $ "Foo" ++ nameBase n) [] Nothing [] [] -- example function
pure $ map mkD e1
|]
Note also the use of lift
; the result of $(mkCons ty)
has type [OtherData]
(in this case) but lift
gives you the TH AST corresponding to those constructors.
Also note that the functions above use the Eq
, Lift
and MyClass
instances of the given type. Due to the stage restriction, you have to define these instances in a seperate module than the use of the splice. So the following won't work:
module A where
import TH (mkSome)
data OtherData = A | B | C deriving (Lift, Eq)
instance MyClass OtherData where
specialValue = C
$( $(mkSome ''OtherData) )
You must use it like so:
-- A.hs
module A where
data OtherData = A | B | C deriving (Lift, Eq)
instance MyClass OtherData where
specialValue = C
-- B.hs
module B where
import TH (mkSome)
import A
$( $(mkSome ''OtherData) )
The result:
mkSome ''OtherData
======>
do { e1_adJ0 <- mapM
lift [c_adJ2 | c_adJ2 <- [A, B, C], (c_adJ2 /= specialValue)];
let mkD_adJ1 (ConE n_adJ3)
= DataD
[] (mkName $ ("Foo" ++ (nameBase n_adJ3))) [] Nothing [] [];
(pure $ (map mkD_adJ1 e1_adJ0)) }
(do { e1_adJ0 <- mapM
lift [c_adJ2 | c_adJ2 <- [A, B, C], (c_adJ2 /= specialValue)];
let mkD_adJ1 (ConE n_adJ3)
= DataD
[] (mkName $ ("Foo" ++ (nameBase n_adJ3))) [] Nothing [] [];
(pure $ (map mkD_adJ1 e1_adJ0)) })
======>
data FooA
data FooB
Upvotes: 2