goldfirere / units

The home of the units Haskell package
95 stars 19 forks source link

evalType fails for Volume (and other such types) #34

Closed norm2782 closed 9 years ago

norm2782 commented 10 years ago

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.

goldfirere commented 9 years ago

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.

goldfirere commented 9 years ago

I just had a dirty thought. Because Q wraps IO, TH code can actually fire up GHCi and do this query itself.

goldfirere commented 9 years ago

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.