如何使用Template Haskell检查多态类型是否存在实例?

时间:2013-11-22 03:34:20

标签: haskell template-haskell

假设,我想检查类型Show是否存在[a]的实例(它确实存在)。

如果我这样做:

let t = ListT `AppT` VarT (mkName "a")
$(stringE . show =<< isInstance ''Show [t])

我收到以下错误:

Not in scope: type variable `a'
In the argument of reifyInstances: GHC.Show.Show [a]
In the expression: $(stringE . show =<< isInstance ''Show [t])
In an equation for `it':
    it = $(stringE . show =<< isInstance ''Show [t])

如果我这样做:

let t' = ForallT [PlainTV (mkName "a")] [ClassP ''Show [VarT (mkName "a")]] t
$(stringE . show =<< isInstance ''Show [t'])

我得到了

"False"

3 个答案:

答案 0 :(得分:3)

你想要做的事实上是在它们拼接之前看这里的类型。如果你在ghci中,你可以输入set -ddump-splices,它会在每个拼接后打印出来编译。然后,用你的例子:

>undefined :: $(return t)
<interactive>:27:16-23: Splicing type
    return t ======> [a]
<interactive>:27:16:
    Not in scope: type variable `a'
    In the result of the splice:
      $(return t)

>undefined :: $(return t')
<interactive>:28:16-24: Splicing type
    return t' ======> forall a b. (Show a, Show b) => [a]

(第二个也会出错 - 但原因不同。你可以在上下文中有一个不在实际类型中的类型变量。这个错误与你的问题基本无关。)

正如您所看到的,[a]类型与类型forall a . [a]相同。当你写(正常)haskell:

func :: [a] 

你实际上在写:

func :: forall a . [a]

但是,模板Haskell不会自动为您插入forall(这将是非常不希望的行为)。和类型

func :: forall . [a] 

会给你一个错误(a is not in scope错误)因为a没有绑定在任何可见范围内,正如人们所期望的那样!类型forall . [a]等同于您在撰写return $ ListT AppT VarT (mkName "a")时由TH生成的类型。

编辑:

如果您愿意只使用具体类型替换类型变量,那么您基本上只是在搜索类的类型。我不知道这是否是你想要的行为,我想我很难找到一个有用的案例。但是,如果您可以轻松检查类型上下文中是否存在类型类实例:

existentialTypeContainsClass :: Name -> Type -> Bool
existentialTypeContainsClass clss (ForallT _ cxt t) = or $ map (boundByPred clss) cxt

boundByPred :: Name -> Pred -> Bool
boundByPred _ (EqualP _ _)    = False
boundByPred c (ClassP clss _) = c == clss

t = ListT `AppT` VarT (mkName "a")
t' = ForallT [PlainTV (mkName "a")] [ClassP ''Show [VarT (mkName "a")]] t

runTest = existentialTypeContainsClass ''Show t'

答案 1 :(得分:2)

从GHC 7.10和模板Haskell 2.10开始, original code provided by Nikita Volkov 实际上是开箱即用的:

> let t = ListT `AppT` VarT (mkName "a")
> $(stringE . show =<< isInstance ''Show [t])
"True"

answer of Nikita Volkov 仍适用于GHC 7.10和TH 2.10。 这是非常优雅的恕我直言, 虽然我更喜欢使用TupleT 0代替ConT ''Int (只是因为它更简单):

> let t = ListT `AppT` TupleT 0
> $(stringE . show =<< isInstance ''Show [t])
"True"

对于GHC 7.10和TH 2.10, 你可以使用这两种方法中的任何一种。 对于GHC≤7.8且TH≤2.9, 你应该使用这个答案中列出的第二种方法。

进一步阅读

答案 2 :(得分:1)

为什么有问题的代码无法解决answer of user2407038

关于解决方案,虽然以下内容并未完全解决问题,但可以将其用作解决方法。可以通过提供某些特定类型而不是变量[a]来检查a。您必须知道您正在检查的课程的某些特定现有实例。如果Show我们知道存在Int的实例 - 我们可以使用它:

Prelude Language.Haskell.TH> let t = ListT `AppT` ConT ''Int
Prelude Language.Haskell.TH> $(stringE . show =<< isInstance ''Show [t])
"True"