2016-11-08 36 views
5

我怎樣才能自動獲得一個Read實例,該GADTs:推導閱讀實例爲GADTs

{-# LANGUAGE GADTs, StandaloneDeriving #-} 

data TypeDec a where 
    TypeDecInt :: TypeDec Int 
    TypeDecString :: TypeDec String 

deriving instance Show (TypeDec a) 

data Bar where 
    Bar :: (Show a, Read a) => TypeDec a -> a -> Bar 

deriving instance Show Bar 

的想法是,Bar是一個序列化的類型。

我可以通過下面的代碼片段或秒差距寫Read實例,但考慮到我有許多相似的類型在不同的模塊這種TypeDecBar是一個樣板:

instance Read Bar where 
    readsPrec _ s = 
    let (bar, tup) = second breaks . breaks $ s 
    in if "Bar" == bar then uncurry parse tup else [] 
    where 
     parse :: String -> String -> [(Bar, String)] 
     parse tdec = case tdec of 
     "TypeDecInt" -> parse' TypeDecInt 
     "TypeDecString" -> parse' TypeDecString 
     _    -> const [] 

     parse' :: (Show a, Read a) => TypeDec a -> String -> [(Bar, String)] 
     parse' tdec s = [(Bar tdec (read s), "")] 

     breaks :: String -> (String, String) 
     breaks = second (drop 1) . break (== ' ') 
+2

這是完全可以使用模板Haskell。人們只需要使用@dfeuer提到的方法(即使用'case'而不是'GHC.Read.choose')。 – Alec

+0

@Alec你知道用模板Haskell派生'Read'實例的示例/教程嗎? – homam

+0

[This](https://wiki.haskell.org/Template_haskell/Instance_deriving_example)看起來不錯。 – Alec

回答

4

我不知道的通用解決方案。也就是說,通過定義readPrec方法(特別參見Text.ReadText.ParserCombinators.ReadPrec獲取一些額外功能,並且可能獲得更多Text.ParserCombinators.ReadP)而不是使用Parsec或定義readsPrec來編寫Read實例會容易得多。這不是你能寫的最快的速度,但它應該相當快。

import Text.Read 

instance Read Bar where 
    readListPrec = readListPrecDefault 

    readPrec = parens $ do 
    Ident "Bar" <- lexP 
    Ident td <- parens lexP 
    case td of 
     "TypeDecInt" -> Bar TypeDecInt <$> readPrec 
     "TypeDecString" -> Bar TypeDecString <$> readPrec 
     _ -> empty 

如果TypeDec是更爲複雜,你有TypeDec IntTypeDec String(使用FlexibleInstances或輔助類)Read情況下,那麼你可能會希望更多的東西模塊化:

instance Read Bar where 
    readListPrec = readListPrecDefault 
    readPrec = parens $ do 
    Ident "Bar" <- lexP 
    (Bar <$> readPrec <*> (readPrec :: ReadPrec Int)) 
     <|> (Bar <$> readPrec <*> (readPrec :: ReadPrec String)) 

注意在第二個例子中,GHC自己無法弄清楚替代品應該具備哪些類型,但是當我們修正第二個字段的類型時,推理會將其傳播到第一個字段。所以在第一種選擇中,我們只會尋找"TypeDecInt",而在第二種情況下,我們只會尋找"TypeDecString"


dependent-sum包定義概括您的Bar一個類型。

data DSum tag f = forall a . !(tag a) :=> f a 

它還定義了使用一些額外的類DSum一個Read實例。一般的方法看起來很穩固,但我建議兩個更改。首先是使用ReadPrec,而不是與列表混雜。第二個是用下面(更簡單)存在一個替換上位GReadResult類型:

data GReadResult t = forall a . GReadResult (t a) 

當然,這些變化將迫使你實現它自己,不過沒關係。

+0

'dependent-sum'是一個有趣的圖書館。但是從它的[例子](https://github.com/mokus0/dependent-sum/blob/master/examples/FooGADT.hs#L72)看來,我仍然需要定義'GRead'實例(並且編寫一些樣板文件對於'tag'類型的每個構造函數)。 – homam

+0

@homam,是的,'DSum'方法只是對實例施加一定的結構;除非你想使用相關工具,否則它不會贏得太多。我只是覺得值得一提。 – dfeuer