2016-02-22 85 views
2

对于复杂的GADT,GHC不能派生出ReadShow,所以我试图定义低于满足read . show == id的自定义实例。我尽可能简化了示例(虽然仍像我的真实代码一样触发运行时错误)。我决定让GHC通过为每个GADT构造函数制作newtype包装器(更精确地说:对于GADT输出的每个类型)来完成ReadShow实例的繁重工作。在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) 

FooU是一个复杂的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,所以我怀疑我做了一些不正确的事情,虽然我没有看到那是什么。

我的问题是:

  1. 为什么会出现这个错误,并逻辑我该如何解决? (有几种方法可以捅破上面的最小示例来使错误消失,但我不能在我的真实代码中执行这些操作。)
  2. 是否存在与GADT不同的/更好的高级方法Read
+0

如果您使用'导出实例Read(t r)=> Read(U t rep r)'?会发生什么?那不被接受?如果将GADT定义为只有两个参数'tr'和'rep',并且定义了一个新类型以将't'与'r'结合起来,生活会更容易吗? – dfeuer

+0

@dfeuer我认为这不适用于我的示例,但即使它的确如此,但它绝对不适用于我的真实代码。 – crockeea

+0

我的意思是'Show',而不是'Read'。 'Show'更容易推导出来。 – dfeuer

回答

1

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

> 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中的定义,在其他情况下也可以这样做。或者,可以使用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 
+0

我正在寻找一些直觉。 *为什么*我应该定义'showsPrec'而不是'show'?对于派生的'Show'实例,我的'U'类型对于GHC派生实例来说太复杂了,这就是我为什么要手动完成它的原因。 – crockeea

+0

我添加了一些链接和解释。 –