goldfirere / th-desugar

Desugars Template Haskell abstract syntax to a simpler format without changing semantics

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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:

addClassCxt class_name tvbs ty = quantifyType $ ForallT tvbs class_cxt ty

This brashly quantifies all type variables, but we really need to make a distinction between class-bound and method-bound variables here.