使用模板Haskell获取关联类型同义词

259

模板Haskell能否找出类型类中声明的关联类型同义词的名称和/或声明?我期望reify可以实现我想要的功能,但它似乎没有提供所有必要的信息。它可用于获取函数类型签名:

% ghci
GHCi, version 7.8.3: http://www.haskell.org/ghc/  :? for help
...
Prelude> -- I'll be inserting line breaks and whitespace for clarity
Prelude> -- in all GHCi output.
Prelude> :set -XTemplateHaskell 
Prelude> import Language.Haskell.TH
Prelude Language.Haskell.TH> class C a where f :: a -> Int
Prelude Language.Haskell.TH> putStrLn $(stringE . show =<< reify ''C)
ClassI (ClassD [] Ghci1.C [PlainTV a_1627398388] []
               [SigD Ghci1.f
                     (ForallT [PlainTV a_1627398388]
                              [ClassP Ghci1.C [VarT a_1627398388]]
                              (AppT (AppT ArrowT (VarT a_1627398388))
                                    (ConT GHC.Types.Int)))])
       []

然而,将关联类型同义词添加到类中不会对输出产生任何更改(除重命名外):

Prelude Language.Haskell.TH> :set -XTypeFamilies 
Prelude Language.Haskell.TH> class C' a where type F a :: * ; f' :: a -> Int
Prelude Language.Haskell.TH> putStrLn $(stringE . show =<< reify ''C')
ClassI (ClassD [] Ghci3.C' [PlainTV a_1627405973] []
               [SigD Ghci3.f'
                     (ForallT [PlainTV a_1627405973]
                              [ClassP Ghci3.C' [VarT a_1627405973]]
                              (AppT (AppT ArrowT (VarT a_1627405973))
                                    (ConT GHC.Types.Int)))])
       []

如果我知道F的名称,我可以查找与其相关的信息。
Prelude Language.Haskell.TH> putStrLn $(stringE . show =<< reify ''F)
FamilyI (FamilyD TypeFam
                 Ghci3.F
                 [PlainTV a_1627405973]
                 (Just StarT))
        []

但我首先找不到 F 的名称。即使我添加了类型类的一个实例,InstanceD 也没有关于定义的任何信息:

Prelude Language.Haskell.TH> instance C' [a] where type F [a] = a ; f' = length
Prelude Language.Haskell.TH> f' "Haskell"
7
Prelude Language.Haskell.TH> 42 :: F [Integer]
42
Prelude Language.Haskell.TH> putStrLn $(stringE . show =<< reify ''C')
ClassI (ClassD [] Ghci3.C' [PlainTV a_1627405973] []
               [SigD Ghci3.f'
                     (ForallT [PlainTV a_1627405973]
                              [ClassP Ghci3.C' [VarT a_1627405973]]
                              (AppT (AppT ArrowT (VarT a_1627405973))
                                    (ConT GHC.Types.Int)))])
       [InstanceD []
                  (AppT (ConT Ghci3.C')
                        (AppT ListT (VarT a_1627406161)))
                  []]

如果 reify 不起作用,除了手动列出联合类型同义词外,是否有其他解决方法?
这个问题存在于 GHC 7.8.3 版本的 template-haskell 包中的版本 2.9.0.0 中;它也存在于 GHC 7.4.2 版本的 template-haskell 包中的版本 2.7.0.0 中。(我没有在 GHC 7.6.* 上检查,但我想它也存在这个问题。)我对任何版本的 GHC(包括“只有在 GHC 版本 V 中修复了此问题”)都感兴趣的解决方案。

2
你看过 reifyInstances 吗? - Kwarrtz
2
@Kwarrtz:我刚试了一下,但是它不起作用;它只会产生与“reify”中看到的相同的“InstanceD”:putStrLn $(stringE . show =<< reifyInstances''C'=<< sequence [[t | [Int] |]])评估为[InstanceD [] (AppT (ConT Ghci1.C') (AppT ListT (VarT a_1627405978))) []],其中缺少类型族实例。 - Antal Spector-Zabusky
1
我觉得 reify 不返回必要的信息有点奇怪。也许 show 隐藏了部分信息?你试过直接检查 Info 对象吗? - Kwarrtz
@Kwarrtz:恐怕InfoShow实例只是派生的,DecShow实例也是一样。不过,正如你所要求的那样,我也可以直接检查,结果是否定的:putStrLn $(reify ''C' >>= \i -> case i of ClassI (ClassD _ _ _ _ [SigD _ _]) _ -> stringE "just a SigD" ; _ -> stringE "something else")会产生just a SigD——这确实是ClassD中唯一的[Dec]!(需要LambdaCase)。我同意这很奇怪;这就是我问这个问题的原因 :-) - Antal Spector-Zabusky
1
@Abel:我认为我们的意见是一致的 - 你最初的评论说这还不足以吸引一个杰出的想法,但它确实吸引了Yuras的回答!我完全同意什么是一个好的答案 :-) - Antal Spector-Zabusky
显示剩余2条评论
1个回答

14

因为没有人请求,所以它没有被实现。

奇怪的是 TH 使用其自己的 AST,而不遵循内部编译器的 AST。 因此,任何新特性(例如关联类型族)都不能通过 TH 自动获得。必须有人打开票并实现它。

参考资料:内部的 reifyClass 函数忽略了 关联类型族(它是由 classExtraBigSig 返回的元组的第5个元素,请参见 ClassATItem 的定义)。

从技术上讲,在 reify 中实现关联类型族支持应该很容易,但最有可能需要破坏向后兼容的 TH API 更改,例如因为其 AST 似乎不支持关联类型默认值。

添加:现在已经实现了(没有 API 更改),可能会在下一个 ghc 发布版中提供。


1
@AntalS-Z 我的意思是 FamilyD 不支持关联类型同义词默认值。你可能没有使用它们,但完整的解决方案可能需要 API 更改。 - Yuras
5
@Abel,将赏金保持开放直到最后也有助于好的答案吸引投票,因此这是一种比快速奖励好答案更有效的奖励方式。 - dfeuer
1
赏金期已过。这是最好的(也是唯一的)答案,除非错误报告#10891得到解决。在您的答案中包含错误报告链接可能是个好主意。 - Abel
1
FYI,#10891已经修复,正在等待合并。 - sinan
1
据我所知,ghc开发人员希望在不破坏TH API的情况下自由更改内部AST。可能还有其他原因。 - Yuras
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接