[GHC] #15824: Prefix/infix distinction in TemplateHaskell types is lost

GHC ghc-devs at haskell.org
Sun Oct 28 20:08:09 UTC 2018


#15824: Prefix/infix distinction in TemplateHaskell types is lost
-------------------------------------+-------------------------------------
        Reporter:  int-index         |                Owner:  (none)
            Type:  bug               |               Status:  new
        Priority:  normal            |            Milestone:
       Component:  Template Haskell  |              Version:  8.6.1
      Resolution:                    |             Keywords:
Operating System:  Unknown/Multiple  |         Architecture:
                                     |  Unknown/Multiple
 Type of failure:  None/Unknown      |            Test Case:
      Blocked By:                    |             Blocking:
 Related Tickets:  #15815, #15760    |  Differential Rev(s):
       Wiki Page:                    |
-------------------------------------+-------------------------------------
Description changed by int-index:

Old description:

> 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`:
>
> {{{
> 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: [https://github.com/glguy/th-
> abstraction/blob/5123c6d054d0949cb444590269f13e5d44699ab2/src/Language/Haskell/TH/Datatype.hs#L1156-L1160
> decomposeType from th-desugar].
>
> This issue is in part responsible for #15815, see comment:5:ticket:15815.

New description:

 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:
 [https://github.com/glguy/th-
 abstraction/blob/5123c6d054d0949cb444590269f13e5d44699ab2/src/Language/Haskell/TH/Datatype.hs#L1156-L1160
 decomposeType from th-desugar].

 This issue is in part responsible for #15815, see comment:5:ticket:15815.

--

-- 
Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/15824#comment:1>
GHC <http://www.haskell.org/ghc/>
The Glasgow Haskell Compiler


More information about the ghc-tickets mailing list