假设,我想检查类型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"
答案 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"