如何为此GADT自动派生Read
个实例:
{-# 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
是一个可序列化的类型。
我可以通过以下代码段或Parsec编写Read
实例,但考虑到我在不同模块中有许多类似于TypeDec
和Bar
的类型,这是一个样板:< / p>
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 (== ' ')
答案 0 :(得分:5)
我不知道通用的解决方案。也就是说,通过定义Read
方法来编写readPrec
个实例要容易得多(有关一些额外函数,请参阅Text.Read
,Text.ParserCombinators.ReadPrec
,也许Text.ParserCombinators.ReadP
甚至更多)而不是使用Parsec
或定义readsPrec
。这不是你写的绝对最快,但它应该相当快。
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
更复杂,并且您有Read
和TypeDec Int
TypeDec String
个实例(使用FlexibleInstances
或辅助类),那么您''我可能想要更模块化的东西:
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
它还使用一些额外的类为Read
定义DSum
个实例。一般方法看起来很稳固,但我建议两个更改。第一种是使用ReadPrec
而不是与列表混淆。第二种是用以下(更简单的)存在性类型替换更高级别的GReadResult
类型:
data GReadResult t = forall a . GReadResult (t a)
当然,这些变化会迫使你自己实施,但没关系。