[GHC] #11345: Template Haskell's handling of infix GADT constructors is broken

GHC ghc-devs at haskell.org
Wed Jan 6 03:55:13 UTC 2016


#11345: Template Haskell's handling of infix GADT constructors is broken
-------------------------------------+-------------------------------------
        Reporter:  RyanGlScott       |                Owner:  RyanGlScott
            Type:  bug               |               Status:  new
        Priority:  normal            |            Milestone:
       Component:  Template Haskell  |              Version:  8.1
      Resolution:                    |             Keywords:
Operating System:  Unknown/Multiple  |         Architecture:
 Type of failure:  Incorrect result  |  Unknown/Multiple
  at runtime                         |            Test Case:
      Blocked By:                    |             Blocking:
 Related Tickets:                    |  Differential Rev(s):
       Wiki Page:                    |
-------------------------------------+-------------------------------------
Changes (by RyanGlScott):

 * owner:   => RyanGlScott


Comment:

 Actually, I realized that I was a little too careless in writing solutions
 2 and 3—to the point where I skipped over an important detail: `InfixC`
 and `reifyFixity` serve distinct purposes. `InfixC` just tells you that a
 constructor was ''declared'' infix rather than prefix, i.e., it's the
 difference between

 {{{#!hs
 data T1 a = T1 a a
 }}}

 and

 {{{#!hs
 data T2 a = a `T2` a
 }}}

 But it tells you nothing about its actual fixity ''when used as an infix
 operator''. In fact, calling `reifyFixity` on both `'T1` and `'T2` would
 yield the same answer (`Fixity 9 InfixL`) since neither have an explicit
 fixity declaration.

 This is a minor distinction, but an important one. As a motivating
 example, when a datatype has a derived `Show` instance:

 * If one of its constructors is declared prefix, then the constructor will
 be shown before its fields, and the fields will always be shown with
 precedence 11. This is regardless of whether a fixity declaration is
 present.
 * If one of its constructors is declared infix, then the constructor will
 be shown between its two arguments, and the fields will shown according to
 the precedence in the fixity declaration plus 1 (if none is present, it
 defaults to 9+1).

 This is extremely important for GADTs because it is a special case. A GADT
 constructor is only considered to be declared infix if (a) it is an
 operator symbol, (b) it has two arguments, (c) it has a fixity
 declaration. Only then would a derived `Show` instance for a GADT
 constructor show the constructor between its arguments.

 For these reasons, proposals 2 and 3 above aren't quite accurate:

 * `InfixC` can't be subsumed under `NormalC` neatly because they discern a
 property of the datatype declaration that can't necessarily be gleaned
 from `reifyFixity`. Unless you were to add another field to `NormalC` to
 mark whether it was declared infix, that it—but that is another breaking
 change.
 * `InfixC` can't be subsumed under `RecC` at all, since if a constructor
 has records, it is automatically considered not to be declared infix.

 Considering all this, proposal 1 looks by far the most attractive in terms
 of ease of implementation and the least API changes (barring
 `GadtC`/`RecGadtC`). I think I'll go ahead and add an `InfixGadtC`
 constructor to `Con`. Any objections?

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


More information about the ghc-tickets mailing list