2016-02-22 84 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

我添加了一些鏈接和解釋。 –