我有函数(让我们称之为mkSome
),它使用Template Haskell构造一些数据类型。它具有典型的签名Name -> Q [Dec]
。
在它的身体某处,我正在提取另一种类型的构造函数
模式匹配:
case tyCons of
DataD ctx nm tyVars mbKind cs derivs -> ...
那些构造函数cs
的类型实例化了这样的类:
class MyClass a where
specialValue :: a
所以,我正在迭代那些cs
,但我想跳过其中一个。{
等于specialValue
。像这样:
[c | c <- cs, c /= specialValue]
示例:
data OtherData = A | B | C
instance MyClass OtherData where
specialValue = C
$(mkSome ''OtherData) -- mkSome must skip C-constructor!
如何在模板Haskell中执行此操作(使用Con
类型:c
是吗?当然,我不能简单地调用构造函数来将创建的值与specialValue
进行比较,因为它是AST节点,而不是真正的构造函数
答案 0 :(得分:1)
这完全取决于您希望如何使用此表达式。你可以写例如
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] |]
这是一个拼接,其结果是除ty
之外的specialValue
的构造函数。
但是如果你想在拼接中操纵结果列表(例如为除specialValue
之外的所有构造函数生成一些代码),那么情况要复杂得多。你需要一个嵌套的拼接来操纵上面拼接的结果:
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
|]
还要注意lift
的使用; $(mkCons ty)
的结果具有类型[OtherData]
(在本例中),但lift
为您提供与这些构造函数对应的TH AST。
另请注意,上述函数使用给定类型的Eq
,Lift
和MyClass
个实例。由于阶段限制,您必须在单独的模块中定义这些实例,而不是使用拼接。所以下面的工作没有成功:
module A where
import TH (mkSome)
data OtherData = A | B | C deriving (Lift, Eq)
instance MyClass OtherData where
specialValue = C
$( $(mkSome ''OtherData) )
你必须这样使用它:
-- 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) )
结果:
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