读取时解析错误

Parse error when reading

GHC 无法为复杂的 GADT 导出 ReadShow,因此我尝试在下面定义满足 read . show == id 的自定义实例。我尽可能地简化了示例(同时仍然像我的真实代码一样触发运行时错误)。我决定让 GHC 为每个 GADT 构造函数制作 newtype 包装器(更准确地说:为每个 type 由 GADT 输出)。在 Read/Show 实例中,我只是 read/show newtype 包装器并在必要时进行转换。我认为这是万无一失的:我让 GHC 定义所有非平凡的实例。不过,我好像做错了什么。

在我的真实代码中,下面的 Foo 是一个复杂的 GADT,GHC 不会派生出 ShowRead。由于 Foo 是一个新类型的包装器(部分),我为此使用派生的 Show/Read 实例。

{-# LANGUAGE FlexibleContexts, FlexibleInstances, GADTs, ScopedTypeVariables #-}

import Text.Read (Read(readPrec))

newtype Bar r = Bar r deriving (Show, Read)
newtype Foo r = Foo (Bar r)
-- use the GHC-derived Show/Read for Bar
instance (Show r) => Show (Foo r) where
  show (Foo x) = show x
instance (Read r) => Read (Foo r) where
  readPrec = Foo <$> readPrec

此实例似乎有效:我可以调用 read . show 并取回输入。 现在我有一个围绕 Foo:

的包装器
data U t rep r where
  U1  :: t r -> U t Int r
  U2 :: t r -> U t Char r
-- use the Read/Show instances for U1Wrap and U2Wrap
newtype U1Wrap t r = U1Wrap {unU1Wrap :: t r} deriving (Show, Read)
newtype U2Wrap t r = U2Wrap (t r) deriving (Show, Read)
instance (Read (t r)) => Read (U t Int r) where
  readPrec = (U1 . unU1Wrap) <$> readPrec
instance (Read (U2Wrap t r)) => Read (U t Char r) where
  readPrec = do
    x <- readPrec
    return $ case x of
      (U2Wrap y) -> U2 y
instance (Show (t r)) => Show (U t Int r) where
  show (U1 x) = show $ U1Wrap x
instance (Show (t r)) => Show (U t Char r) where
  show (U2 x) = show (U2Wrap x :: U2Wrap t r)

Foo 一样,U 是一个复杂的 GADT,因此我为 GADT 的每个输出类型定义了自定义的新类型包装器。不幸的是,我的 Show/Read 实例不起作用:

main :: IO ()
main = do
  let x = U1 $ Foo $ Bar 3
      y = U2 $ Foo $ Bar 3
  print $ show (read (show x) `asTypeOf` x)
  print $ show (read (show y) `asTypeOf` y)

main 打印第一行,但我在第二行得到 Exception: Prelude.read: no parse

这是我第一次使用 Read,所以我怀疑我做错了什么,虽然我看不出那是什么。

我的问题是:

  1. 为什么我会收到此错误,逻辑上我该如何解决? (有几种方法可以戳上面的最小示例来使错误消失,但我不能在我的真实代码中做那些事情。)
  2. 是否有 different/better 高级方法来 Reading GADT?

您为 Foo 自定义的 Show 实例括号不正确。

> U2 $ Foo $ Bar 3
U2Wrap Bar 3

编写自定义 Show 实例时,您应该改为定义 showsPrec。那是因为 show 只是返回一个独立于上下文的字符串,而 showsPrec 基于优先级插入括号。有关更多文档,请参阅 Text.Show

instance (Show r) => Show (Foo r) where
 showsPrec n (Foo x) = showsPrec n x

在这里有效。

我不知道有什么优雅的方法可以自动为我们获取此 GADTRead 实例。派生机制似乎无法弄清楚每个 rep 只需要考虑一个构造函数。

这里至少可以推导出Show。我还在此处包括一个手动 Read 实例,我希望它符合 Show。我试图模仿 Text.Read 中的定义,您也可以在其他情况下这样做。或者,可以使用 -ddump-deriv GHC 参数查看派生的 Read 实例,并尝试在自定义代码中复制它们。

{-# LANGUAGE GADTs, StandaloneDeriving, FlexibleInstances, FlexibleContexts #-}

import Text.Read
import Data.Proxy

data U t rep r where
  U1 :: t r -> U t Int r
  U2 :: t r -> U t Char r

deriving instance Show (t r) => Show (U t rep r)

instance Read (t r) => Read (U t Int r) where
  readPrec = parens $ do
    prec 10 $ do
      Ident "U1" <- lexP
      U1 <$> readPrec

instance Read (t r) => Read (U t Char r) where
  readPrec = parens $ do
    prec 10 $ do
      Ident "U2" <- lexP
      U2 <$> readPrec