如何从Template Haskell调用构造函数

时间:2017-12-11 13:06:14

标签: haskell template-haskell

我有函数(让我们称之为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节点,而不是真正的构造函数

1 个答案:

答案 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。

另请注意,上述函数使用给定类型的EqLiftMyClass个实例。由于阶段限制,您必须在单独的模块中定义这些实例,而不是使用拼接。所以下面的工作没有成功:

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