Haskell (一般)从自定义数据类型生成解析器?

Haskell (一般)从自定义数据类型生成解析器?,haskell,Haskell,我正在开发一个需要与服务器对话的网络流式客户端。服务器用bytestrings对响应进行编码,例如,“1\NULJohn\NULTeddy\NUL501\NUL”,其中“\NUL”是分隔符。上述响应转化为“这是类型1的消息(由服务器硬编码),它告诉客户端用户的ID是什么(这里,“John Teddy”的用户ID是“501”) 我天真地定义了一个自定义数据类型 data User { firstName :: String , lastName :: String , id :: In

我正在开发一个需要与服务器对话的网络流式客户端。服务器用bytestrings对响应进行编码,例如,“1\NULJohn\NULTeddy\NUL501\NUL”,其中“\NUL”是分隔符。上述响应转化为“这是类型1的消息(由服务器硬编码),它告诉客户端用户的ID是什么(这里,“John Teddy”的用户ID是“501”)

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

data User
  { firstName :: String
  , lastName :: String
  , id :: Int
  }
parseID :: Parser User
parseID = ...
以及此数据类型的解析器

data User
  { firstName :: String
  , lastName :: String
  , id :: Int
  }
parseID :: Parser User
parseID = ...
然后,在解析器成功地对这样的响应进行匹配之后,只需编写一个处理程序来执行某些工作(例如,写入数据库)


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

对于这类问题,我转向使用泛型而不是直接使用泛型。泛型sop构建在泛型之上,提供了以统一方式操作记录中所有字段的函数

在这个回答中,我使用了base附带的解析器,但是任何其他
应用程序的
解析器都可以。一些初步导入:

{-# 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
我们定义了一个typeclass,它可以为每个实例生成一个
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)
让我们检查一下是否可以使用
recParser
解析
Foo

main :: IO ()
main = do
    print $ readP_to_S (recParser @Foo) "55False"
结果是

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

您可以编写自己的解析器,但是已经有一个包可以为您进行解析:虽然SO通常不是搜索库建议的地方,但我想为那些正在寻找解决方案的人提供这个答案,但他们没有时间自己实现,也没有时间去寻找现成的解决方案。

{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE OverloadedStrings #-}

import Data.Csv
import Data.Vector
import Data.ByteString.Lazy as B
import GHC.Generics

data Person = P { personId :: Int
                , firstName :: String
                , lastName :: String
                } deriving (Eq, Generic, Show)

 -- the following are provided by friendly neighborhood Generic
instance FromRecord Person
instance ToRecord Person

main :: IO ()
main = do B.writeFile "test" "1\NULThomas\NULof Aquin"
          Right thomas <- decodeWith (DecodeOptions 0) NoHeader <$> 
                              B.readFile "test"

          print (thomas :: Vector Person)
{-#派生通用语言}
{-#语言重载字符串}
导入数据.Csv
导入数据。矢量
将Data.ByteString.Lazy作为B导入
进口GHC.仿制药
data Person=P{personId::Int
,firstName::String
,lastName::String
}派生(等式、通用、显示)
--以下内容由友好社区提供
记录人的实例
实例记录人
main::IO()
main=do B.writeFile“test”1\NULThomas\NULof Aquin

正确的thomas Generics可以做到这一点。您可以在类似attoparsec的基础上构建一个通用解析器,使用
Parseable
typeclass为实现
generic
的任何内容提供默认实现。然后您只需要
instance Parseable User where
就可以对其进行解析。很高兴知道。我在哪里可以找到更多细节?我做了谷歌“阿托帕塞克通用帕塞布”“但是,搜索结果没有太大帮助。
Parsable
将是您自己编写的类型类。Attoparsec是一个在分析ByTestRing方面相当出色的库。Generic是一个内置的typeclass,它提供了一些函数,用于获取可在代码中操作的数据类型的泛型表示。例如,aeson提供了一个
FromJSON
typeclass,它可以利用
Generic
,这样您就可以执行
实例FromJSON MyType,其中
无需任何额外的工作,就可以将JSON解析为
MyType
@user2812201。类似的想法,除了编码而不是解码。谢谢你。所以我按照parse::ByteString->Parser Parsable和
数据用户的
类Parsable做了一些事情。。。派生泛型
和实例可解析用户,其中parse=…
?我将检查aeson的src代码并找出它。