对于复杂的GADT,GHC不能派生出Read
或Show
,所以我试图定义低于满足read . show == id
的自定义实例。我尽可能简化了示例(虽然仍像我的真实代码一样触发运行时错误)。我决定让GHC通过为每个GADT构造函数制作newtype
包装器(更精确地说:对于GADT输出的每个类型)来完成Read
和Show
实例的繁重工作。在Read
/Show
实例中,我只需读取/显示newtype包装器并在必要时进行转换。我认为这是万无一失的:我让GHC定义所有不平凡的实例。但是,我似乎做错了什么。解析读取时出错
在我的真实代码中,Foo
下面是一个复杂的GADT,GHC不会派生出Show
或Read
。由于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,所以我定义自定义NEWTYPE包装的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
,所以我怀疑我做了一些不正确的事情,虽然我没有看到那是什么。
我的问题是:
- 为什么会出现这个错误,并逻辑我该如何解决? (有几种方法可以捅破上面的最小示例来使错误消失,但我不能在我的真实代码中执行这些操作。)
- 是否存在与GADT不同的/更好的高级方法
Read
?
如果您使用'导出实例Read(t r)=> Read(U t rep r)'?会发生什么?那不被接受?如果将GADT定义为只有两个参数'tr'和'rep',并且定义了一个新类型以将't'与'r'结合起来,生活会更容易吗? – dfeuer
@dfeuer我认为这不适用于我的示例,但即使它的确如此,但它绝对不适用于我的真实代码。 – crockeea
我的意思是'Show',而不是'Read'。 'Show'更容易推导出来。 – dfeuer