Read1/Show1 instances for Complex use incorrect precedence
The Read1
and Show1
instances for Complex
handle precedence in a way that is inconsistent with Complex
's Read
and Show
instances. This is best illustrated by example:
module Main (main) where
import Data.Complex (Complex(..))
import Data.Functor.Classes (readPrec1, showsPrec1)
import Text.ParserCombinators.ReadPrec (readPrec_to_S)
import Text.Read (Read(..))
comp :: Complex Int
comp = 1 :+ 1
compareInstances :: Int -> IO ()
compareInstances p = do
let precBanner = " (at precedence " ++ show p ++ ")"
putStrLn $ "Read vs. Read1" ++ precBanner
print (readPrec_to_S readPrec p "1 :+ 1" :: [(Complex Int, String)])
print (readPrec_to_S readPrec1 p "1 :+ 1" :: [(Complex Int, String)])
putStrLn ""
putStrLn $ "Show vs. Show1" ++ precBanner
putStrLn $ showsPrec p comp ""
putStrLn $ showsPrec1 p comp ""
putStrLn ""
main :: IO ()
main = do
compareInstances 6
compareInstances 7
If you run this with GHC 9.2.1-alpha1, it will produce the following output:
Read vs. Read1 (at precedence 6)
[(1 :+ 1,"")]
[(1 :+ 1,"")]
Show vs. Show1 (at precedence 6)
1 :+ 1
1 :+ 1
Read vs. Read1 (at precedence 7)
[]
[(1 :+ 1,"")]
Show vs. Show1 (at precedence 7)
(1 :+ 1)
1 :+ 1
At precedence 6, the Read1
/Show1
instances behave analogously to the Read
/Show
instances, as expected. At precedence 7, however, they produce different results! In particular:
- The
Read
instance will fail to parse"1 :+ 1"
at precedence 7, since it should be surrounded by parentheses at that precedence due to:+
's fixity of 6. TheRead1
instance, however, will incorrectly parse the same string without the required parentheses. - The
Show
instance will print1 :+ 1
with surrounding parentheses at precedence 7, again because of:+
's fixity of 6. TheShow1
instance, however, will incorrectly omit these parentheses.
Both issues are caused by the Read1
/Show1
instances incorrectly assuming that :+
has a fixity of 9, rather than its actual fixity of 6. Because the Read
and Show
instances are derived, it automatically uses the correct fixity, but the handwritten Read1
/Show1
instances do not mirror this. This could be fixed as simply as:
diff --git a/libraries/base/Data/Functor/Classes.hs b/libraries/base/Data/Functor/Classes.hs
index 6a0d008982..d672c340d7 100644
--- a/libraries/base/Data/Functor/Classes.hs
+++ b/libraries/base/Data/Functor/Classes.hs
@@ -848,11 +848,13 @@ instance Eq1 Complex where
-- [(2 % 3 :+ 3 % 4,"")]
--
instance Read1 Complex where
- liftReadPrec rp _ = parens $ prec 9 $ do
+ liftReadPrec rp _ = parens $ prec complexPrec $ do
x <- step rp
expectP (Symbol ":+")
y <- step rp
return (x :+ y)
+ where
+ complexPrec = 6
liftReadListPrec = liftReadListPrecDefault
liftReadList = liftReadListDefault
@@ -863,8 +865,10 @@ instance Read1 Complex where
-- "2 :+ 3"
--
instance Show1 Complex where
- liftShowsPrec sp _ d (x :+ y) = showParen (d >= 10) $
- sp 10 x . showString " :+ " . sp 10 y
+ liftShowsPrec sp _ d (x :+ y) = showParen (d > complexPrec) $
+ sp (complexPrec+1) x . showString " :+ " . sp (complexPrec+1) y
+ where
+ complexPrec = 6
-- Building blocks
These instances were introduced in GHC 9.2, so there's still time to fix them before the final 9.2.1 release. Patch incoming.