[Git][ghc/ghc][wip/jeltsch/text-read-implementation-into-base] Move the `Text.Read` implementation into `base`
Wolfgang Jeltsch pushed to branch wip/jeltsch/text-read-implementation-into-base at Glasgow Haskell Compiler / GHC Commits: 17eef35d by Wolfgang Jeltsch at 2026-05-04T15:38:57+03:00 Move the `Text.Read` implementation into `base` - - - - - 10 changed files: - libraries/base/src/Data/Functor/Classes.hs - libraries/base/src/Data/Functor/Compose.hs - libraries/base/src/Prelude.hs - libraries/base/src/Text/Read.hs - libraries/ghc-internal/ghc-internal.cabal.in - libraries/ghc-internal/src/GHC/Internal/IO/Encoding.hs - − libraries/ghc-internal/src/GHC/Internal/Text/Read.hs - testsuite/tests/th/T24111.stdout - testsuite/tests/typecheck/should_compile/subsumption_sort_hole_fits.stderr - testsuite/tests/typecheck/should_fail/T21130.stderr Changes: ===================================== libraries/base/src/Data/Functor/Classes.hs ===================================== @@ -85,7 +85,7 @@ import GHC.Internal.Read (expectP, list, paren, readField) import GHC.Internal.Show (appPrec) import GHC.Internal.Text.ParserCombinators.ReadPrec (ReadPrec, readPrec_to_S, readS_to_Prec, pfail) -import GHC.Internal.Text.Read (Read(..), parens, prec, step, reset) +import Text.Read (Read(..), parens, prec, step, reset) import GHC.Internal.Text.Read.Lex (Lexeme(..)) import GHC.Internal.Text.Show (showListWith) import Prelude ===================================== libraries/base/src/Data/Functor/Compose.hs ===================================== @@ -35,7 +35,7 @@ import GHC.Internal.Data.Foldable (Foldable(..)) import GHC.Internal.Data.Monoid (Sum(..), All(..), Any(..), Product(..)) import GHC.Internal.Data.Type.Equality (TestEquality(..), (:~:)(..)) import GHC.Generics (Generic, Generic1) -import GHC.Internal.Text.Read (Read(..), ReadPrec, readListDefault, readListPrecDefault) +import Text.Read (Read(..), ReadPrec, readListDefault, readListPrecDefault) import Prelude infixr 9 `Compose` ===================================== libraries/base/src/Prelude.hs ===================================== @@ -179,7 +179,7 @@ import GHC.Internal.Data.Tuple import GHC.Internal.Base hiding ( foldr, mapM, sequence ) import GHC.Internal.Classes import GHC.Internal.Err -import GHC.Internal.Text.Read +import Text.Read import GHC.Internal.Enum import GHC.Internal.Num import GHC.Internal.Prim (seq) ===================================== libraries/base/src/Text/Read.hs ===================================== @@ -39,5 +39,84 @@ module Text.Read readMaybe ) where -import GHC.Internal.Text.Read +import GHC.Err (errorWithoutStackTrace) +import GHC.Read + ( + ReadS, + Read (readsPrec, readList, readPrec, readListPrec), + lex, + readParen, + readListDefault, + lexP, + parens, + readListPrecDefault + ) +import Control.Monad (return) +import Data.Function (id) +import Data.Maybe (Maybe (Nothing, Just)) +import Data.Either (Either (Left, Right), either) +import Data.String (String) +import Text.Read.Lex (Lexeme (Char, String, Punc, Ident, Symbol, Number, EOF)) +import Text.ParserCombinators.ReadP (skipSpaces) import Text.ParserCombinators.ReadPrec + +-- $setup +-- >>> import Prelude + +------------------------------------------------------------------------ +-- utility functions + +-- | equivalent to 'readsPrec' with a precedence of 0. +reads :: Read a => ReadS a +reads = readsPrec minPrec + +-- | Parse a string using the 'Read' instance. +-- Succeeds if there is exactly one valid result. +-- A 'Left' value indicates a parse error. +-- +-- >>> readEither "123" :: Either String Int +-- Right 123 +-- +-- >>> readEither "hello" :: Either String Int +-- Left "Prelude.read: no parse" +-- +-- @since base-4.6.0.0 +readEither :: Read a => String -> Either String a +readEither s = + case [ x | (x,"") <- readPrec_to_S read' minPrec s ] of + [x] -> Right x + [] -> Left "Prelude.read: no parse" + _ -> Left "Prelude.read: ambiguous parse" + where + read' = + do x <- readPrec + lift skipSpaces + return x + +-- | Parse a string using the 'Read' instance. +-- Succeeds if there is exactly one valid result. +-- +-- >>> readMaybe "123" :: Maybe Int +-- Just 123 +-- +-- >>> readMaybe "hello" :: Maybe Int +-- Nothing +-- +-- @since base-4.6.0.0 +readMaybe :: Read a => String -> Maybe a +readMaybe s = case readEither s of + Left _ -> Nothing + Right a -> Just a + +-- | The 'read' function reads input from a string, which must be +-- completely consumed by the input process. 'read' fails with an 'error' if the +-- parse is unsuccessful, and it is therefore discouraged from being used in +-- real applications. Use 'readMaybe' or 'readEither' for safe alternatives. +-- +-- >>> read "123" :: Int +-- 123 +-- +-- >>> read "hello" :: Int +-- *** Exception: Prelude.read: no parse +read :: Read a => String -> a +read s = either errorWithoutStackTrace id (readEither s) ===================================== libraries/ghc-internal/ghc-internal.cabal.in ===================================== @@ -329,7 +329,6 @@ Library GHC.Internal.System.Posix.Types GHC.Internal.Text.ParserCombinators.ReadP GHC.Internal.Text.ParserCombinators.ReadPrec - GHC.Internal.Text.Read GHC.Internal.Text.Read.Lex GHC.Internal.Text.Show GHC.Internal.Type.Reflection ===================================== libraries/ghc-internal/src/GHC/Internal/IO/Encoding.hs ===================================== @@ -46,7 +46,7 @@ import GHC.Internal.IO.Encoding.Types import qualified GHC.Internal.IO.Encoding.Iconv as Iconv #else import qualified GHC.Internal.IO.Encoding.CodePage as CodePage -import GHC.Internal.Text.Read (reads) +import GHC.Internal.Text.Read (readsPrec) #endif import qualified GHC.Internal.IO.Encoding.Latin1 as Latin1 import qualified GHC.Internal.IO.Encoding.UTF8 as UTF8 @@ -319,7 +319,8 @@ mkTextEncoding' cfm enc = _ | isAscii -> return (Latin1.mkAscii cfm) _ | isLatin1 -> return (Latin1.mkLatin1_checked cfm) #if defined(mingw32_HOST_OS) - 'C':'P':n | [(cp,"")] <- reads n -> return $ CodePage.mkCodePageEncoding cfm cp + 'C':'P':n | [(cp,"")] <- readsPrec 0 n -> return $ CodePage.mkCodePageEncoding cfm cp + -- 'readsPrec 0' is the same as 'reads', but 'reads' is only defined in @base@. _ -> unknownEncodingErr (enc ++ codingFailureModeSuffix cfm) #else -- Otherwise, handle other encoding needs via iconv. ===================================== libraries/ghc-internal/src/GHC/Internal/Text/Read.hs deleted ===================================== @@ -1,115 +0,0 @@ -{-# LANGUAGE Trustworthy #-} -{-# LANGUAGE NoImplicitPrelude #-} - ------------------------------------------------------------------------------ --- | --- Module : GHC.Internal.Text.Read --- Copyright : (c) The University of Glasgow 2001 --- License : BSD-style (see the file libraries/base/LICENSE) --- --- Maintainer : libraries@haskell.org --- Stability : provisional --- Portability : non-portable (uses Text.ParserCombinators.ReadP) --- --- Converting strings to values. --- --- The "Text.Read" library is the canonical library to import for --- 'Read'-class facilities. For GHC only, it offers an extended and much --- improved 'Read' class, which constitutes a proposed alternative to the --- Haskell 2010 'Read'. In particular, writing parsers is easier, and --- the parsers are much more efficient. --- ------------------------------------------------------------------------------ - -module GHC.Internal.Text.Read ( - -- * The 'Read' class - Read(..), - ReadS, - - -- * Haskell 2010 functions - reads, - read, - readParen, - lex, - - -- * New parsing functions - module GHC.Internal.Text.ParserCombinators.ReadPrec, - L.Lexeme(..), - lexP, - parens, - readListDefault, - readListPrecDefault, - readEither, - readMaybe - - ) where - -import GHC.Internal.Base (String, id, return) -import GHC.Internal.Err (errorWithoutStackTrace) -import GHC.Internal.Maybe (Maybe(..)) -import GHC.Internal.Read -import GHC.Internal.Data.Either -import GHC.Internal.Text.ParserCombinators.ReadP as P -import GHC.Internal.Text.ParserCombinators.ReadPrec -import qualified GHC.Internal.Text.Read.Lex as L - --- $setup --- >>> import Prelude - ------------------------------------------------------------------------- --- utility functions - --- | equivalent to 'readsPrec' with a precedence of 0. -reads :: Read a => ReadS a -reads = readsPrec minPrec - --- | Parse a string using the 'Read' instance. --- Succeeds if there is exactly one valid result. --- A 'Left' value indicates a parse error. --- --- >>> readEither "123" :: Either String Int --- Right 123 --- --- >>> readEither "hello" :: Either String Int --- Left "Prelude.read: no parse" --- --- @since base-4.6.0.0 -readEither :: Read a => String -> Either String a -readEither s = - case [ x | (x,"") <- readPrec_to_S read' minPrec s ] of - [x] -> Right x - [] -> Left "Prelude.read: no parse" - _ -> Left "Prelude.read: ambiguous parse" - where - read' = - do x <- readPrec - lift P.skipSpaces - return x - --- | Parse a string using the 'Read' instance. --- Succeeds if there is exactly one valid result. --- --- >>> readMaybe "123" :: Maybe Int --- Just 123 --- --- >>> readMaybe "hello" :: Maybe Int --- Nothing --- --- @since base-4.6.0.0 -readMaybe :: Read a => String -> Maybe a -readMaybe s = case readEither s of - Left _ -> Nothing - Right a -> Just a - --- | The 'read' function reads input from a string, which must be --- completely consumed by the input process. 'read' fails with an 'error' if the --- parse is unsuccessful, and it is therefore discouraged from being used in --- real applications. Use 'readMaybe' or 'readEither' for safe alternatives. --- --- >>> read "123" :: Int --- 123 --- --- >>> read "hello" :: Int --- *** Exception: Prelude.read: no parse -read :: Read a => String -> a -read s = either errorWithoutStackTrace id (readEither s) ===================================== testsuite/tests/th/T24111.stdout ===================================== @@ -3,6 +3,6 @@ pattern (:+_0) :: GHC.Internal.Types.Int -> (GHC.Internal.Types.Int, GHC.Internal.Types.Int) pattern x_1 :+_0 y_2 = (x_1, y_2) pattern A_0 :: GHC.Internal.Types.Int -> GHC.Internal.Base.String -pattern A_0 n_1 <- (GHC.Internal.Text.Read.read -> n_1) where +pattern A_0 n_1 <- (Text.Read.read -> n_1) where A_0 0 = "hi" A_0 1 = "bye" ===================================== testsuite/tests/typecheck/should_compile/subsumption_sort_hole_fits.stderr ===================================== @@ -11,14 +11,13 @@ subsumption_sort_hole_fits.hs:2:5: warning: [GHC-88464] [-Wtyped-holes (in -Wdef words :: String -> [String] (imported from ‘Prelude’ (and originally defined in ‘GHC.Internal.Data.OldList’)) - read :: forall a. Read a => String -> a - with read @[String] - (imported from ‘Prelude’ - (and originally defined in ‘GHC.Internal.Text.Read’)) repeat :: forall a. a -> [a] with repeat @String (imported from ‘Prelude’ (and originally defined in ‘GHC.Internal.List’)) + read :: forall a. Read a => String -> a + with read @[String] + (imported from ‘Prelude’ (and originally defined in ‘Text.Read’)) mempty :: forall a. Monoid a => a with mempty @(String -> [String]) (imported from ‘Prelude’ ===================================== testsuite/tests/typecheck/should_fail/T21130.stderr ===================================== @@ -6,6 +6,9 @@ T21130.hs:10:6: error: [GHC-88464] In an equation for ‘x’: x = (_ f) :: Int • Relevant bindings include x :: Int (bound at T21130.hs:10:1) Valid hole fits include + read :: forall a. Read a => String -> a + with read @Int + (imported from ‘Prelude’ (and originally defined in ‘Text.Read’)) head :: forall a. GHC.Internal.Stack.Types.HasCallStack => [a] -> a with head @Int (imported from ‘Prelude’ @@ -14,10 +17,6 @@ T21130.hs:10:6: error: [GHC-88464] with last @Int (imported from ‘Prelude’ (and originally defined in ‘GHC.Internal.List’)) - read :: forall a. Read a => String -> a - with read @Int - (imported from ‘Prelude’ - (and originally defined in ‘GHC.Internal.Text.Read’)) T21130.hs:10:8: error: [GHC-39999] • Ambiguous type variable ‘t0’ arising from a use of ‘f’ View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/17eef35d2d56271d0360a8adb8ea93ac... -- View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/17eef35d2d56271d0360a8adb8ea93ac... You're receiving this email because of your account on gitlab.haskell.org.
participants (1)
-
Wolfgang Jeltsch (@jeltsch)