Skip to content
GitLab
Projects Groups Snippets
  • /
  • Help
    • Help
    • Support
    • Community forum
    • Submit feedback
  • Sign in / Register
  • GHC GHC
  • Project information
    • Project information
    • Activity
    • Labels
    • Members
  • Repository
    • Repository
    • Files
    • Commits
    • Branches
    • Tags
    • Contributors
    • Graph
    • Compare
    • Locked Files
  • Issues 5,249
    • Issues 5,249
    • List
    • Boards
    • Service Desk
    • Milestones
    • Iterations
  • Merge requests 581
    • Merge requests 581
  • CI/CD
    • CI/CD
    • Pipelines
    • Jobs
    • Schedules
    • Test Cases
  • Deployments
    • Deployments
    • Releases
  • Analytics
    • Analytics
    • Value stream
    • CI/CD
    • Code review
    • Insights
    • Issue
    • Repository
  • Wiki
    • Wiki
  • Snippets
    • Snippets
  • Activity
  • Graph
  • Create a new issue
  • Jobs
  • Commits
  • Issue Boards
Collapse sidebar
  • Glasgow Haskell CompilerGlasgow Haskell Compiler
  • GHCGHC
  • Issues
  • #22816
Closed
Open
Issue created Jan 23, 2023 by Ryan Scott@RyanGlScottMaintainer

Compose's Show/Show1 and Read/Read1 instances no longer agree in GHC 9.6.1-alpha1

As of base-4.18.0.0 (as bundled with GHC 9.6.1-alpha1), the behavior of Compose's Show instance no longer agrees with its Show1 instance. By that, I mean that if you run the following program:

module Main where

import Data.Functor.Classes
import Data.Functor.Compose

ex :: Compose Maybe Maybe Int
ex = Compose Nothing

main :: IO ()
main = do
  putStrLn $ showsPrec  0 ex ""
  putStrLn $ showsPrec1 0 ex ""

You would expect Compose Nothing to be printed twice. Indeed, this happens with GHC 9.4.4 and earlier:

$ runghc-9.4.4 Bug.hs
Compose Nothing
Compose Nothing

But not with GHC 9.6.1-alpha1:

$ runghc-9.6.0.20230111 Bug.hs
Compose {getCompose = Nothing}
Compose Nothing

The Show instance now prints out the getCompose record selector, which it did not do in previous releases.

My guess is that this is an oversight that was accidentally introduced in commit 7beb356e. The previous Show instance for Compose was defined as:

instance (Show1 f, Show1 g, Show a) => Show (Compose f g a) where
    showsPrec = showsPrec1

instance (Show1 f, Show1 g) => Show1 (Compose f g) where
    liftShowsPrec sp sl d (Compose x) =
        showsUnaryWith (liftShowsPrec sp' sl') "Compose" d x
      where
        sp' = liftShowsPrec sp sl
        sl' = liftShowList sp sl

Note that this uses showsUnaryWith, which does not print the record selector. After commit 7beb356e, however, the Show instance for Compose is now derived:

deriving instance Show (f (g a)) => Show (Compose f g a)

Derived Show instances always show record selectors if they are defined, which would explain the difference. One possible to fix the issue would be to change the Show instance to:

instance Show (f (g a)) => Show (Compose f g a) where
    showsPrec d (Compose x) =
        showsUnaryWith showsPrec "Compose" d x

A similar bug exists for Compose's now-derived Read instance, which also disagrees with its Read1 instance. This can be seen by running this program:

module Main where

import Data.Functor.Compose

main :: IO ()
main = print (read "Compose Nothing" :: Compose Maybe Maybe Int)

This succeeds on GHC 9.4.4 and earlier:

$ runghc-9.4.4 Bug.hs
Compose Nothing

But fails with GHC 9.6.1-alpha1:

$ runghc-9.6.0.20230111 Bug.hs
Compose {getCompose = Bug.hs: Prelude.read: no parse

cc @Ericson2314

Edited Jan 24, 2023 by Ryan Scott
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information
Assignee
Assign to
Time tracking