reifyInstances doesn't find instances defined in the same module
Summary
reifyInstances
has some limitations/bugs in what it can find.
Given
discoverInstances :: Name -> Q Exp
discoverInstances name = do
instances <- reifyInstances name [VarT (mkName "a")]
lift $ map show instances
I'd expect it to return a list of all instances of the given name that are in scope at the splice site. However, I get some different behavior.
Error on class defined in the same module
If I have:
class D a
instancesOfD = $(discoverInstances ''D)
then I get an error from GHC:
/home/matt/Projects/reify-instances-bu/test/Spec.hs:15:11: error:
• GHC internal error: ‘C’ is not in scope during type checking, but it passed the renamer
tcl_env of environment: [a3mJ :-> Type variable ‘a’ = a :: k0]
• In the type ‘C a’
In the argument of reifyInstances: Main.C a
In the untyped splice: $(discoverInstances ''C)
|
15 | say $(discoverInstances ''C)
| ^^^^^^^^^^^^^^^^^^^^^
(the error message text isn't exact; see the repro repo for more information and reproducible messages)
Fail to find instances defined in module
If I write:
module Lib where
class C a
module Main where
instance C Int
instance C Double
data X = X
instance C X
main = print $(discoverInstances ''C)
then I get an empty list. I would expect it to find instances that are defined in the module.
Instances defined elsewhere are OK
If I move the above instances to Lib
, everything works out alright:
module Lib where
class C a
instance C Int
instance C Double
module Main where
data X = X
instance C X -- not an issue with orphans
main = print $(discoverInstances ''C)
This prints out the C Int
and C Double
instances, but not C X
.
Steps to reproduce
I've created a repository that documents the behavior I've seen.
https://github.com/parsonsmatt/reify-instances-bug
Expected behavior
I expect that reifyInstances
discovers all instances in scope, based on the documentation:
{- | @reifyInstances nm tys@ returns a list of visible instances of @nm tys@. That is,
if @nm@ is the name of a type class, then all instances of this class at the types @tys@
are returned. Alternatively, if @nm@ is the name of a data family or type family,
all instances of this family at the types @tys@ are returned.
Note that this is a \"shallow\" test; the declarations returned merely have
instance heads which unify with @nm tys@, they need not actually be satisfiable.
- @reifyInstances ''Eq [ 'TupleT' 2 \``AppT`\` 'ConT' ''A \``AppT`\` 'ConT' ''B ]@ contains
the @instance (Eq a, Eq b) => Eq (a, b)@ regardless of whether @A@ and
@B@ themselves implement 'Eq'
- @reifyInstances ''Show [ 'VarT' ('mkName' "a") ]@ produces every available
instance of 'Eq'
There is one edge case: @reifyInstances ''Typeable tys@ currently always
produces an empty list (no matter what @tys@ are given).
-}
The word visible
above may mean something different than I have in mind.
Environment
- GHC version used: 8.0, 8.2, 8.10.4, 9.0.1, 9.2.0.20210422
(tried to test with 7.10.3 but the ghcup version doesn't work with fPIE or something)
Optional:
- Operating System: Ubuntu 20.04
- System Architecture: x86_64