Prefix/infix distinction in TemplateHaskell types is lost
Consider data T a b = a :. b
.
In the declaration, :.
is mapped to InfixC
:
ghci> putStrLn $(reify ''T >>= stringE . show)
TyConI (DataD [] T [KindedTV a StarT,KindedTV b StarT] Nothing [InfixC (Bang NoSourceUnpackedness NoSourceStrictness,VarT a) :. (Bang NoSourceUnpackedness NoSourceStrictness,VarT b)] [])
In expressions, a :. b
is mapped to InfixE
:
ghci> runQ [e| 1 :+ 2 |] >>= print
InfixE (Just (LitE (IntegerL 1))) (ConE :+) (Just (LitE (IntegerL 2)))
In patterns, a :. b
is mapped to InfixP
:
ghci> runQ [p| 1 :. 2 |] >>= print
InfixP (LitP (IntegerL 1)) :. (LitP (IntegerL 2))
In types, a :. b
is mapped to InfixT
:
ghci> runQ [t| 1 :. 2 |] >>= print
InfixT (LitT (NumTyLit 1)) (PromotedT :.) (LitT (NumTyLit 2))
That last one was a lie. In reality, in types a :. b
is mapped to nested AppT
, as if it was written as (:.) a b
:
ghci> runQ [t| 1 :. 2 |] >>= print
AppT (AppT (PromotedT :.) (LitT (NumTyLit 1))) (LitT (NumTyLit 2))
This is despite the existence of InfixT
.
The same issue can be observed when reifying types:
ghci> type A = 1 :. 2
ghci> putStrLn $(reify ''A >>= stringE . show)
TyConI (TySynD A [] (AppT (AppT (PromotedT :.) (LitT (NumTyLit 1))) (LitT (NumTyLit 2))))
This is not specific to infix constructors and can be observed with any infix (type) operators.
It's best to change this in the same release as we fix #15760, as there is code in the wild that is prepared to face neither InfixT
nor ParensT
, and it would break silently. RyanGlScott gives an example of such code: decomposeType from th-desugar.
This issue is in part responsible for #15815 (closed), see ticket:15815#comment:162853.