gpt4 book ai didi

haskell - 派生 GADT 的读取实例

转载 作者:行者123 更新时间:2023-12-02 01:53:46 30 4
gpt4 key购买 nike

如何自动派生此 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 实例,但考虑到我有许多与 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 (== ' ')

最佳答案

我不知道通用的解决方案。也就是说,通过定义 readPrec 方法来编写 Read 实例要容易得多(尤其是 Text.ReadText.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 更复杂,并且您有 TypeDec IntTypeDec StringRead 实例(使用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

它还使用一些额外的类为 DSum 定义了一个 Read 实例。总体方法看起来很可靠,但我建议进行两处更改。第一个是使用 ReadPrec 而不是忙于列表。第二种是将较高级别的 GReadResult 类型替换为以下(更简单的)存在类型:

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

当然,这些更改将迫使您自己实现,但没关系。

关于haskell - 派生 GADT 的读取实例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40497225/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com