Reputation: 43340
Suppose, I want to check whether an instance of Show
exists for type [a]
(which it does).
If I do this:
let t = ListT `AppT` VarT (mkName "a")
$(stringE . show =<< isInstance ''Show [t])
I get the following error:
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])
If I then do this:
let t' = ForallT [PlainTV (mkName "a")] [ClassP ''Show [VarT (mkName "a")]] t
$(stringE . show =<< isInstance ''Show [t'])
I get
"False"
Upvotes: 2
Views: 590
Reputation: 6480
Starting with GHC 7.10 and Template Haskell 2.10, the original code provided by Nikita Volkov actually works out-of-the-box:
> let t = ListT `AppT` VarT (mkName "a")
> $(stringE . show =<< isInstance ''Show [t])
"True"
The answer of Nikita Volkov
still works on GHC 7.10 and TH 2.10.
It is pretty elegant IMHO,
though I prefer using TupleT 0
in place of ConT ''Int
(just because it is a simpler type):
> let t = ListT `AppT` TupleT 0
> $(stringE . show =<< isInstance ''Show [t])
"True"
For GHC 7.10 and TH 2.10, you can use any of the two methods. For GHC ≤ 7.8 and TH ≤ 2.9, you should use the second method listed in this answer.
Upvotes: 2
Reputation: 43340
Why the code in question doesn't work is explained in answer of user2407038.
Concerning the solution, while the following doesn't completely solve the problem, it can be used as a workaround. The [a]
can be checked by supplying some specific type instead of variable a
. You must know some specific existing instances for the class you're checking. In case of Show
we know that there exists an instance for Int
- we can use that:
Prelude Language.Haskell.TH> let t = ListT `AppT` ConT ''Int
Prelude Language.Haskell.TH> $(stringE . show =<< isInstance ''Show [t])
"True"
Upvotes: 2
Reputation: 14623
What you would like to do is actually see the types here before they are splices in. If you are in ghci, you can type set -ddump-splices
which will print each splice after it is compiled. Then, with your example:
>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]
(The second one also gives an error - but for a different reason. You can have a type variable in the context that is not in the actual type. This error is essentially unrelated to your question).
As you can see the type [a]
is not the same as the type forall a . [a]
. When you write in (normal) haskell:
func :: [a]
You are actually writing:
func :: forall a . [a]
However, Template Haskell doesn't insert forall
s for you automatically (this would be very undesired behaviour). And the type
func :: forall . [a]
will give you an error (the a is not in scope
error) because a
has not been bound in any visible scope, as one would expect! And the type forall . [a]
is equivalent to the type generated by TH when you wrote return $ ListT
AppTVarT (mkName "a")
.
edit:
If you are willing to just replace type vars with concrete types, then you are essentially just searching the type for your class. I don't know if this is your desired behavior, I guess I am struggling to find a case where this is useful. But if you can easily check if a typeclass instance is present in the context of a type:
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'
Upvotes: 3