创建有效构造函数列表

时间:2017-04-21 02:23:56

标签: haskell gadt

请考虑以下事项:

{-# 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]

基本上x1D T1的所有有效构造函数的列表,x2D T2的所有有效构造函数的列表。

但是,我希望这两个列表能够反映添加到D的任何其他构造函数,我不想像现在这样对这些列表进行硬编码。

有没有办法定义x1x2,以便从D自动生成它们?

1 个答案:

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