precedence bug with derived instances

Dean Herington heringto@cs.unc.edu
Fri, 18 Oct 2002 11:50:29 -0400


Simon Peyton-Jones wrote:

> | In GHC 5.04.1, derived instances of Show mishandle precedence:
> |
> | Prelude> putStrLn (showsPrec 10 (Just 0) "")
> | Just 0
> |
> | The result should be:   (Just 0)
>
> I think it's a bug in the Report, not in GHC, actually.  The Report says
> (Section D.4)
>
>   "The function 'showsPrec d x r' accepts a precedence level 'd'
>   (a number from 0 to 10), a value 'x', and a string 'r'.
>      ....
>   "The representation will be enclosed in parentheses if the precedence
>   of the top-level constructor operator in 'x' is less than 'd'.  Thus,
>   if 'd' is 0 then the result is never surrounded in parentheses; if
>   'd' is 10 it is always surrounded in parentheses, unless it is an
>   atomic expression."
>
> But in fact, the precedence of function application is 10, as the
> syntax makes clear.  Indeed, the example code at the end of Appendix D
> shows a call to showsPrec with an argument of 11 (it's written
> "app_prec + 1"), for precisely this reason.
>
> So, I think that I have to make these changes to the Report:
>
> (A) The last sentence should say "..; if 'd' is 11 it is always
> surrounded in parentheses, unless it is an atomic expression (recall,
> function application has precedence 10)".
>
> (B) The earlier parenthesis should say "(a number from 0 to 11)".
>
> Does anyone disagree?

I agree.  I note that GHC recently (5.04.1?) changed to match the changes
you propose above, and Hugs (as of Dec. 2001 version) still matches the
current report.

I reread Appendix D in the revised report
(<http://research.microsoft.com/Users/simonpj/haskell98-revised/haskell98-report-html/derived.html>)
and have some additional comments.

(1) In the first section, in:

    instance (cx, cx') => Ci (T u1 ... uk) where { d }

the use of "(cs, cs')" is a bit loose (that is, suggestive rather than
precise syntax).  One can't (according to the report, though GHC seems to
allow it) have nested parentheses in a context, which would occur if cx
included parentheses.

(2) In section D.1, in:

    For example, False < _|_ is _|_, even though False is the first
constructor of the Bool type.

change the operator from "<" to "<=".  There is no reason to expect (False <
_|_) to be True.

(3) Section D.4 gives the expected rule:

    head (readsPrec d (showsPrec d x "")) == (x,"")

Why should this not be:

    readsPrec d (showsPrec d x "") == [(x,"")]

(4) I don't understand this paragraph in section D.4:

    readsPrec will parse any valid representation of the standard types
apart from lists, for which only the bracketed form [...] is accepted. See
Appendix A for full details.

The statement seems to say that ((readsPrec 5 (show
"abc"))::[([Char],String)]) is invalid, but that can't be what it means to
say.

(5) The next paragraph of section D.4 says:

    Spaces and parentheses are only added where needed, ignoring
associativity.

The phrase "only where needed" suggests that (show (1 :$ 2 :$ NT)) should
produce the string "1:$(2:$NT)" (no spaces) rather than the string "1 :$ (2
:$ NT)", as claimed later in the section (and borne out by GHC).  It needs
to be stated that an infix operator is preceded and followed by a space.

(6) In the bullet that begins "The derived Read instance allows", change
"parenthese" to "parentheses".

 -- Dean