evalType fails for Volume (and other such types)
norm2782 opened this issue · 3 comments
Since 806a27d (issue #22), the evalType
TH function is available to evaluate types as far as possible. This works well for, e.g., Mass
and Length
. However, the following instance fails:
instance Show $(evalType [t| Volume |]) where
Giving the following error:
evalType [t| Volume |]
======>
Qu ((@*) ((GHC.Types.:) (F Data.Metrology.SI.Dims.Length (S Zero)) GHC.Types.[]) (S (S (S Zero)))) DefaultLCSU Double
src/NutriDB/AbsSyn.hs:68:10:
Illegal type synonym family application in instance:
Qu
('['F Data.Metrology.SI.Dims.Length ('S 'Zero)]
@* 'S ('S ('S 'Zero)))
'DefaultLCSU
Double
In the instance declaration for
‘Show (Qu ((@*) ((:) (F Data.Metrology.SI.Dims.Length (S Zero)) []) (S (S (S Zero)))) DefaultLCSU Double)’
Failed, modules loaded: NutriDB.DefaultValue.
In fact, all types defined using the :/
combinator (and possibly others, such as :^
) fail with the same error. While these types are indeed more complex than Mass
and Length
, they at first glance don't appear to be very polymorphic, suggesting that it may be possible to use evalType
with types such as Volume
as well.
Unfortunately, this bug resurfaces with GHC 7.10. This is because GHC 7.10 is more scrupulous about including kind signatures when reifying type families -- sometimes omitting the kind signature can yield the wrong behavior. This is surely an improvement in GHC. However, it causes a conundrum for th-desugar. With kind signatures in type family patterns, th-desugar would have to do kind matching when figuring out how to simplify closed type families. This would include type inference, something we're surely not doing. So, I don't see how to fix this, I'm afraid.
Perhaps the best solution is to expose some ability to pass a TH expression through GHC's solver. But that will have to wait for GHC 7.12. Your best bet, if you're caught by this, is to fire up GHCi, and use its :kind!
command, like so:
λ> import Data.Metrology.SI
λ> :kind! Volume
Volume :: *
= Qu
'['F Data.Metrology.SI.Dims.Length ('S Two)] 'DefaultLCSU Double
:kind!
evaluates out a type family as far as it can go. Then, just copy and paste the output into your instance declaration. This is far from optimal, but also far from dead-in-the-water.
I just had a dirty thought. Because Q
wraps IO
, TH code can actually fire up GHCi and do this query itself.
I just had another, rather less dirty thought. It would seem (without looking at the code) that th-desugar could export a expandTypeNoKinds
function that just skips kind-checking. This would be unsafe, in that it might sometimes do the wrong thing. But for units
, it would solve this problem, as units
doesn't use kind-polymorphism in a way that would be dangerous here.
This seems quite workable.