Reputation: 2754
I want to define a Haskell module that exports a class C
and instances of C
for various types. Because I want to define a lot of instances, that can be defined automatically following a given scheme, I defined a helper TH function to define the instance somewhat like this:
module Foo (C) where
class C a
defineCInstance :: TypeQ -> DecsQ
defineCInstance t =
[d|
instance C $t
|]
defineCInstance [t| () |]
defineCInstance [t| Char |]
defineCInstance [t| Int |]
However, the example above doesn't compile, because due to stage restrictions, it is not allowed to use defineCInstance
in a splice in the same module it is defined in. This can be fixed by moving defineCInstance
into a new module (let's call it Foo.Internal
) and importing it in Foo
. However when I just move defineCInstance
into another module it won't have C
in scope and I currently see two ways to work around this.
C
into Foo.Internal
as wellIf I move both defineCInstance
and C
into Foo.Internal
defineCInstance
can easily refer to C
and I can splice defineCInstance
in Foo
without problems. However, the instance defined in Foo
will not be defined in the same module as C
anymore and thus become orphan instances. I would like to avoid this, because then I would have to silence the warning for orphans, which is AFAIK only possible for the whole file and might silence the warning for other unintended orphan instances in the same file. I also noticed that haddock then lists all the instances as orphan instances in the documentation, which I would like to avoid as well.
defineCInstance
refer to C
directly.Instead of referring to instance C
inside defineCInstance
I could use something like instance $(conT $ mkName "C")
instead. However this hides the dependency between C
and defineCInstance
from the compiler, which makes errors more likely. For example, if I rename C
, but forget to change the name in defineCInstance
the compiler will still happily compile defineCInstance
. Even worse, it relies on the correct C
being in scope at the splice site of defineCInstance
. If the user has the wrong C
in scope, the generated code will be completely nonsensical.
Is there some way to define C
, defineCInstance
and the instances for C
in a way, which still allows to refer to C
directly from defineCInstance
and avoids defining orphan instances?
Upvotes: 2
Views: 261
Reputation: 33519
Define an internal defineCInstance_
which takes the Name
of C
as a parameter;
In the external Foo
module containing the class, use defineCInstance_ ''Foo.C
for the standard instances;
Export defineCInstance = defineCInstance_ ''Foo.C
, preventing users from using the wrong name.
In step 2 you can put all the relevant types in a list and traverse it all at once, so you only have to refer to the ''Foo.C
name twice: once in the splice, once in the exported defineCInstance
.
Upvotes: 2