th-desugar HEAD incorrectly quantifies class method variables
RyanGlScott opened this issue · comments
I accidentally broke things in #101 in a subtle way that I didn't realize until now. This program demonstrates what I'm talking about:
{-# LANGUAGE TemplateHaskell #-}
module Main where
import Language.Haskell.TH
import Language.Haskell.TH.Desugar
main :: IO ()
main = putStrLn
$([d| class C a where
method :: a -> b -> a
|] >>= \ds -> withLocalDeclarations ds (dsReify (mkName "method"))
>>= stringE . show)
With th-desugar
HEAD, this produces:
Just (DVarI method (DForallT [DPlainTV b_6989586621679242816,DPlainTV a_6989586621679242815] [DAppT (DConT C_6989586621679242813) (DVarT a_6989586621679242815)] (DAppT (DAppT DArrowT (DVarT a_6989586621679242815)) (DAppT (DAppT DArrowT (DVarT b_6989586621679242816)) (DVarT a_6989586621679242815)))) (Just C_6989586621679242813))
After some cleanup, that's
method :: forall b a. C a => a -> b -> a
Contrast this with the behavior of L.H.T.reify
in this program:
{-# LANGUAGE TemplateHaskell #-}
module Main where
import Language.Haskell.TH
class C a where
method :: a -> b -> a
$(pure [])
main :: IO ()
main = putStrLn $(reify 'method >>= stringE . pprint)
Class op from Main.C: Main.method :: forall (a_0 :: *) . Main.C a_0 =>
forall (b_1 :: *) . a_0 -> b_1 -> a_0
GHC's reification always quantifies the class variables, following by the class context, followed by the quantifies method variables, followed by the method type signature. th-desugar
deviates from this convention, however. This is actually causing issues upstream in singletons
, since a couple of places in the codebase assume that th-desugar
reifies class methods according to this convention.
The line of code that needs to be changed is this:
This brashly quantifies all type variables, but we really need to make a distinction between class-bound and method-bound variables here.