gpt4 book ai didi

haskell - (一般)从自定义数据类型构建解析器?

转载 作者:行者123 更新时间:2023-12-03 12:48:30 25 4
gpt4 key购买 nike

我正在开发一个需要与服务器通信的网络流客户端。服务器将响应编码为字节串,例如,“1\NULJohn\NULTeddy\NUL501\NUL”,其中 '\NUL' 是分隔符。上面的响应转换为“这是一条类型 1(由服务器硬编码)的消息,它告诉客户端用户的 ID 是什么(这里,“John Teddy”的用户 ID 是“501”)。

我天真地定义了一个自定义数据类型

data User
{ firstName :: String
, lastName :: String
, id :: Int
}

和这个数据类型的解析器
parseID :: Parser User
parseID = ...

然后,在解析器成功计算出这样的响应后,只需编写一个处理程序来完成一些工作(例如,写入数据库)。这是非常简单的。

但是,服务器有将近 100 种不同的响应,客户端需要解析这样的响应。我怀疑必须有一种更优雅的方式来完成这项工作,而不是像这样编写 100 个几乎相同的解析器,因为毕竟所有 haksell 编码员都是懒惰的。我是泛型编程的新手,所以有人可以告诉我是否有可以完成这项工作的软件包吗?

最佳答案

对于这类问题,我求助于 generics-sop而不是直接使用泛型。 generics-sop 建立在泛型之上,并提供以统一方式操作记录中所有字段的功能。

在这个答案中,我使用了 ReadP带有 base 的解析器,但任何其他 Applicative解析器会做。一些初步进口:

{-# language DeriveGeneric #-}
{-# language FlexibleContexts #-}
{-# language FlexibleInstances #-}
{-# language TypeFamilies #-}
{-# language DataKinds #-}
{-# language TypeApplications #-} -- for the Proxy

import Text.ParserCombinators.ReadP (ReadP,readP_to_S)
import Text.ParserCombinators.ReadPrec (readPrec_to_P)
import Text.Read (readPrec)
import Data.Proxy
import qualified GHC.Generics as GHC
import Generics.SOP

我们定义了一个可以产生 Applicative 的类型类。每个实例的解析器。这里我们只定义了 Int 的实例和 Bool :
class HasSimpleParser c where
getSimpleParser :: ReadP c

instance HasSimpleParser Int where
getSimpleParser = readPrec_to_P readPrec 0

instance HasSimpleParser Bool where
getSimpleParser = readPrec_to_P readPrec 0

现在我们为记录定义一个通用解析器,其中每个字段都有一个 HasSimpleParser实例:
recParser :: (Generic r, Code r ~ '[xs], All HasSimpleParser xs) => ReadP r
recParser = to . SOP . Z <$> hsequence (hcpure (Proxy @HasSimpleParser) getSimpleParser)
Code r ~ '[xs], All HasSimpleParser xs约束意味着“该类型只有一个构造函数,字段类型列表为 xs ,所有字段类型都有 HasSimpleParser 实例”。

hcpure 构造一个 n 元乘积 ( NP ),其中每个组件都是 r 的相应字段的解析器. ( NP 产品将每个组件包装在一个类型构造函数中,在我们的例子中是解析器类型 ReadP )。

然后我们使用 hsequence 将解析器的 n 元乘积转换为 n 元乘积的解析器。

最后,我们 fmap 到结果解析器中,并将 n 元乘积变回原始 r记录使用 to . Z SOP 将 n 元乘积转换为乘积和 to 需要构造函数功能期待。

好的,让我们定义一个示例记录并使其成为 Generics.SOP.Generic 的实例:
data Foo = Foo { x :: Int, y :: Bool } deriving (Show, GHC.Generic)

instance Generic Foo -- Generic from generics-sop

让我们检查是否可以解析 FoorecParser :
main :: IO ()
main = do
print $ readP_to_S (recParser @Foo) "55False"

结果是
[(Foo {x = 55, y = False},"")]

关于haskell - (一般)从自定义数据类型构建解析器?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42583838/

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