Desugared data constructor has incorrect type variable specificity
RyanGlScott opened this issue · comments
In this example:
{-# LANGUAGE GADTs #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneKindSignatures #-}
{-# LANGUAGE TemplateHaskell #-}
module Bug where
import Data.Kind (Type)
import Language.Haskell.TH hiding (Type)
import Language.Haskell.TH.Desugar
type P :: forall {k}. k -> Type
data P a = MkP
$(pure [])
$(do TyConI dec <- reify ''P
[ddec] <- dsDec dec
runIO $ putStrLn $ pprint $ decToTH ddec
pure [])
The type of MkP
, as reported by GHCi's :type
command, is:
λ> :type MkP
MkP :: forall {k} (a :: k). P a
But this is not the same type that is given to MkP
after round-tripping through th-desugar
:
λ> :load Bug.hs
[1 of 1] Compiling Bug ( Bug.hs, interpreted )
data Bug.P (a_0 :: k_1) where
Bug.MkP :: forall k_1 (a_0 :: k_1) . Bug.P (a_0 :: k_1)
Here, k
is specified rather than inferred! What went wrong?
In some sense, the problem lies with reify
. If you replace decToTH ddec
with dec
above, then it reveals what reify ''P
returns:
λ> :load Bug.hs
[1 of 1] Compiling Bug ( Bug.hs, interpreted )
data Bug.P (a_0 :: k_1) = Bug.MkP
Ok, one module loaded.
If this is to be believed, then P
was declared as data P (a :: k) = MkP
, which would suggest that k
is indeed specified. We know this not to be the case, of course. And indeed, this reify
quirk is a deliberate design choice: see the discussion at GHC#22828 for more details.
One way to solve this problem would be to call reifyType 'MkP
, which tells us:
forall {k_0 :: *} (a_1 :: k_0) . Bug.P a_1
This preserves the fact that k
is inferred. We could use the output of reifyType
as a guide when determining whether type variables in a Haskell98-style data constructor are specified or inferred.
A similar bug exists when reifying a locally defined data constructor:
{-# LANGUAGE GADTs #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneKindSignatures #-}
{-# LANGUAGE TemplateHaskell #-}
module Bug where
import Data.Kind (Type)
import Language.Haskell.TH hiding (Type)
import Language.Haskell.TH.Desugar
$(do decs <- [d| type P :: forall {k}. k -> Type
data P (a :: k) = MkP |]
DataConI _ pTy _ <- withLocalDeclarations decs $ reifyWithLocals $ mkName "MkP"
runIO $ putStrLn $ pprint pTy
pure [])
[1 of 1] Compiling Bug ( Bug.hs, interpreted )
forall k_0 (a_1 :: k_0) . P_2 a_1
Again, k_0
should be inferred, not specified.
See also #220, which is in similar territory.