请考虑以下事项:
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE GADTs #-}
data T = T1 | T2
data D (t :: T) where
D1 :: D T1
D2 :: D T2
D3 :: D d
D4 :: D T1
x1 :: [D T1]
x1 = [D1, D3, D4]
x2 :: [D T2]
x2 = [D2, D3]
基本上x1
是D T1
的所有有效构造函数的列表,x2
是D T2
的所有有效构造函数的列表。
但是,我希望这两个列表能够反映添加到D
的任何其他构造函数,我不想像现在这样对这些列表进行硬编码。
有没有办法定义x1
和x2
,以便从D
自动生成它们?
答案 0 :(得分:2)
免责声明 - 我的TemplateHaskell-fu几乎不存在 - 但我已经调查了一下,它应该为您提供一个起点:
对于那些不了解模板的人Haskell是一种允许编写在编译时运行的程序的元编程(语言) - 它是经过类型检查的,所以它是安全的(对于某些安全定义,我认为你可以编写无限时间编译的程序。)
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE TemplateHaskell #-}
import Language.Haskell.TH
data T = T1 | T2
data D (t :: T) where
D1 :: D T1
D2 :: D T2
D3 :: D d
D4 :: D T1
您可以先将文件加载到GHCi(不要忘记:set -XTemplateHaskell
那里)
> typeInfo = reify ''D
> $(stringE . show =<< typeInfo)
typeInfo
是一个Q Info
,可让您从某个类型中提取信息(由''
转义) - $(..)
就像打印一样。
这为您提供了构建(G)ADT的模板haskell表达式:
TyConI (
DataD [] TMP.D [KindedTV t_6989586621679027167 (ConT TMP.T)] Nothing
[GadtC [TMP.D1] [] (AppT (ConT TMP.D) (ConT TMP.T1))
,GadtC [TMP.D2] [] (AppT (ConT TMP.D) (ConT TMP.T2))
,ForallC [KindedTV d_6989586621679027168 (ConT TMP.T)] [] (GadtC [TMP.D3] [] (AppT (ConT TMP.D) (VarT d_6989586621679027168)))
,GadtC [TMP.D4] [] (AppT (ConT TMP.D) (ConT TMP.T1))] [])
我有一些模式匹配 - 你可以找到没有限制(ForallC
)或某种类型(TMP.T1
/ TMP.T2
)的构造函数然后写一些表达式 - 从那些创建新类型。
现在我没有足够的时间来提供 - 但我今晚会更新这个答案。
我在构建类型方面看得更多,但我不得不承认自己有点卡住了 - 我成功解构了类型信息。
d = reify ''D
dataName :: Info -> Maybe [Name]
dataName (TyConI (DataD _ _ _ _ x _) )= Just [t | NormalC t _ <- x]
dataName _ = Nothing
gadtDataUnsafe :: Info -> Q Exp
gadtDataUnsafe (TyConI (DataD _ _ _ _ cons _)) = return $ head $ concat [t | GadtC t _ _ <- cons]
我认为从这里过滤T1
/ T2
/ forall d
是可行的,但是很繁琐但可以构建列表。
我失败的是构建类型 - 如果我将文件加载到ghci中我可以执行
> f = $(gadtDataUnsafe =<< d)
>:t f
f :: D 'T1
但如果我在文件中调用它,我会收到以下错误
error:
• GHC stage restriction:
‘gadtData’ is used in a top-level splice, quasi-quote, or annotation,
and must be imported, not defined locally
• In the untyped splice: $(gadtData =<< d)
我知道,例如Edward Kmett制作了一些魔法制作镜头的东西,它在同一个文件中工作,但拼接没有分配给变量 - 所以也许你需要为你的列表构建名称Q Exp
- 我想mkName
就是你需要的东西。
这总结了我发现的一切 - 我希望它有所帮助,我至少学到了一些东西 - 对于一个完整的答案,也许更聪明/更有经验的模板haskell可以在第二个答案中提供他/她的一些知识。 / p>