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