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

2023-11-23

我正在开发一个需要与服务器通信的网络流媒体客户端。服务器将响应编码为字节串,例如“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 编码员都是懒惰的。我是通用编程的新手,所以有人可以告诉我是否有一个包可以完成这项工作?


对于这类问题我求助于仿制药-sop而不是直接使用泛型。仿制药-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 and 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)

The Code r ~ '[xs], All HasSimpleParser xs约束意味着“该类型只有一个构造函数,字段类型列表为xs,并且所有字段类型都有HasSimpleParser实例”。

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

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

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


好的,让我们定义一个示例记录并将其作为一个实例Generics.SOP.Generic:

data Foo = Foo { x :: Int, y :: Bool } deriving (Show, GHC.Generic)

instance Generic Foo -- Generic from generics-sop

让我们检查一下是否可以解析Foo with recParser:

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

结果是

[(Foo {x = 55, y = False},"")]
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

(一般)从自定义数据类型构建解析器? 的相关文章

  • Haskell printf 转字符串

    Haskell 中有等效的 sprintf 吗 我需要将双精度值转换并格式化为字符串 有没有其他方法而不使用printf什么样的功能 主要问题是要避免 Prelude gt putStrLn myDoubleVal 1 7944444444
  • Haskell 中的常量变量声明

    要声明常量变量 我可以在 Ruby 中执行以下操作 class COLOR RED 10 BLUE 20 GREEM 30 end COLOR RED回报10 COLOR BLUE回报20 等等 我如何在 Haskell 中实现这一点 我想
  • “反向”使用 Maybe Monad

    假设我有很多功能 f a gt Maybe a g a gt Maybe a h a gt Maybe a 我想按以下方式组合它们 如果 f 返回 Nothing 则计算 g 如果 g 返回 Nothing 则计算 h 如果其中任何一个计算
  • 为什么haskell中的递归列表这么慢?

    我对 Haskell 很陌生 我在 Haskell 中定义了一个函数 febs Integral a gt a gt a febs n n lt 0 0 n 1 1 n 2 1 otherwise febs n 1 febs n 2 但是
  • 反应性香蕉时间延迟

    我已经查阅了文档反应香蕉 http hackage haskell org package reactive banana 而且我找不到指定明确时间延迟的方法 举例来说 我想采取Event t a并将其所有发生的事件移至未来 1 秒 或获取
  • 如何处理“恐慌:不可能的事情发生了”并在 Haskell 中继续

    我有以下代码 它使用 GHC API 加载模块并获取表达式的类型 typeObjects String gt String gt IO Type typeObjects modules objects do defaultErrorHand
  • 什么是阴谋地狱?

    在阅读有关 阴谋地狱 的内容时 我有点困惑 因为这个词的含义太多了 我猜最初 Cabal Hell 指的是钻石依赖问题 该问题是通过限制构建计划在每个构建计划中只有任何包的单个版本来解决的 一个包的两个不同版本不能存在于单个构建计划中 正如
  • 管道中缺少 ResourceT 实例

    我在尝试使用时遇到奇怪的错误ResourceT http hackage haskell org package conduit 1 0 9 1 docs Data Conduit html t 3aResourceT来自管道 1 0 9
  • Haskell 和 Idris 之间的区别:类型宇宙中运行时/编译时的反映

    因此 在 Idris 中 编写以下内容是完全有效的 item b Bool gt if b then Nat else List Nat item True 42 item False 1 2 3 cf https www youtube
  • Haskell 中的类型化抽象语法和 DSL 设计

    我正在 Haskell 中设计 DSL 我想要进行赋值操作 像这样的东西 下面的代码只是为了在有限的上下文中解释我的问题 我没有类型检查 Stmt 类型 data Stmt forall a Assign String Exp a Assi
  • 使用 Haskell 将函数注入到 Java .class 文件中

    我使用 Haskell 编写了一个 Java 字节码解析器 它工作得很好 然而下一步让我完全难住了 我的 Haskell 程序需要修改 class 文件 以便在执行时 Java 程序打印 输入 此处的方法名称 在执行方法之前 并且 退出 此
  • 将名称绑定到值与将值分配给变量

    阅读 Bartosz Milewski 的文章完整的 https www fpcomplete com school starting with haskell basics of haskell 3 pure functions lazi
  • 动态加载编译的 Haskell 模块 - GHC 7.6

    我正在尝试使用 GHC API 动态编译和加载 Haskell 模块 我知道 API 从一个版本到另一个版本波动很大 所以我专门谈论 GHC 7 6 我尝试在 MacOS 和 Linux 上运行相同的代码 在这两种情况下 插件模块都可以正常
  • 在 Haskell 中创建 100 万个线程需要多长时间?

    据我了解 Haskell 有绿色线程 但它们的重量有多轻 是否可以创建100万个线程 或者 100 000 个线程需要多长时间 from here http www reddit com r programming comments a4n
  • 是否有适用于 Haskell 或 Scala 等函数式语言的 LL 解析器生成器?

    我注意到明显缺乏用函数式语言创建解析器的 LL 解析器 我一直在寻找但没有成功的理想发现是为 ANTLR 风格的 LL 语法生成 Haskell 解析器 语法的模小数重新格式化 并且令我惊讶的是 每个最后一个解析器生成器都具有函数我发现的语
  • Haskell - 翻转具有两个参数的类型类的参数

    我有一个多参数类型类 它提供了一个可以交换其参数的函数 class Swappable a b where swappable a gt b gt Bool So if a and b form Swappable a b then b a
  • 数据类型变体之间的转换

    假设我想创建一种数据类型的两种变体 一种具有特定的构造函数 另一种没有它 否则它们是相同的 我想出了这个 LANGUAGE KindSignatures LANGUAGE DataKinds LANGUAGE GADTs data Foo
  • 用纯函数式语言保持状态

    我正在尝试弄清楚如何执行以下操作 假设您正在开发直流电机的控制器 您希望让它以用户设置的特定速度旋转 def set point ref sp 90 while true let curr read speed controller set
  • 不明白这个 haskell 代码中的内容

    我有一些 Haskell 代码 我正在尝试完成它 但我不明白其中发生了什么 type Bag a a gt Int emptyB Bag a emptyB e gt 0 countB Eq a gt Bag a gt a gt Int co
  • 如何在 GHCJS 程序中定期执行操作?

    应该有人使用setInterval通过Javascript 或者使用一些更惯用的基于线程的解决方案 Using setInterval posed 一些挑战 https stackoverflow com questions 3357661

随机推荐

  • vsCode java.test.config vmArgs 不工作

    我的项目需要以下 vmArgs 才能运行 vmArgs javaagent lib aspectjweaver 1 9 5 jar javaagent lib spring instrument 5 2 3 RELEASE jar modu
  • file_get_contents():SSL 操作失败,代码为 1,无法启用加密

    我一直在尝试从我在服务器上创建的 PHP 页面访问这个特定的 REST 服务 我将问题缩小到这两行 所以我的 PHP 页面如下所示 该页面在第 2 行终止 并出现以下错误 Warning file get co
  • 如何有效转置二维位矩阵

    我一直在这个问题上绊倒 例如在这个问题 给定一个原始整数类型数组形式的 2D 位矩阵 板 数组 例如一个数组long 为了简单起见 我们可以假设一个方阵 例如 64 的数组long64 位平台上的值long Let x i for 0 lt
  • 为什么 Qt 中的字体显得模糊或像素化?

    我所有的字体都出现像素化 所以我使用AntiAliasing但这没有帮助 正如您在图像本身中看到的像素化字体 这是我当前使用的代码 butt1 QtWidgets QLabel Scrappr font QtGui QFont font s
  • 可以在 contenteditable div 中使用浏览器的撤消功能来撤消“range.insertNode”吗?

    我正在研究一个contenteditablediv 制作一个简单的富文本编辑器 我的要求之一是能够在按钮事件的光标位置插入 html 块 我能够通过使用使该部分正常工作range selection range insertNode nod
  • 反转表行

    我想反转表格正文行使用 jQuery 我拥有的 table width 630 border 0 cellspacing 0 cellpadding 0 thead tr td TITLE A td td TITLE B td 继续 jsf
  • 我可以处理 HTML

    我在 SO 和其他地方看到了很多关于右键单击事件以及如何使用 JavaScript 捕获和处理它们的问题和答案 通常使用 button的属性event浏览器生成的对象 然而 我还没有找到的一件事 可能是因为这是一个非常奇怪的请求 是如何捕获
  • “??”是什么意思?意思是?

    我正在查看 ASP NET MVC 1 0 生成的代码 并且想知道 双问号是什么意思 This constructor is not used by the MVC framework but is instead provided for
  • 在 C# 中使用 XML 文件存储数据

    我基本上是在寻找有人在这方面为我指明正确的方向 我阅读了一些 Microsoft 文档 但这并没有多大帮助 这是我第一次尝试使用 XML 我正在编写一个应用程序 需要存储已知用户的列表以及每个用户创建的别名列表 我已经弄清楚如何在应用程序关
  • Python Django ValueError:源代码字符串不能包含空字节

    我已经放下了一个我已经工作了几个月的 Django 项目 当我尝试重新运行服务器时 我收到了这个错误 ValueError source code string cannot contain null bytes 回溯是 C Users B
  • 在ios7中重新加载tableView标题

    如何在不重新加载所有表的情况下做到这一点 UIView tableView UITableView tableView viewForHeaderInSection NSInteger section UIView header if se
  • 无点风格并使用 $

    如何结合使用 和无点风格 一个明显的例子是下面的实用函数 times Int gt a gt a times n xs concat replicate n xs 只是写concat replicate产生错误 同样你不能写concat r
  • 在 Github 上获取 Travis Shield 以反映所选分支状态

    现在 无论我在 github 项目页面中选择哪个分支 我都可以让 travisshield 反映最新运行或特定分支 我可以通过在 URL 末尾省略或指定分支名称来完成此操作 相反 我想获得与所选分支相对应的盾牌 换句话说 每次我选择不同的分
  • 使用 javascript 取消注释 html 代码

    带有一些注释标签的 Html 表 我只是想取消注释这些标签 我尝试过使用 javascript 的正则表达式 但问题是它删除了整个注释行 因为我只是想取消注释这些标签 下面是带有注释标签的示例 html 表 table tr td ABCD
  • Angular2在第一次点击后删除点击事件绑定

    在我的应用程序中 我有一个甚至可以单击的按钮
  • 流星反应中的时刻?

    一直在使用https github com acreeger meteor moment在流星中 它工作得很好 但是有没有一种方法可以使时刻反应输出 以便它计数 3秒前 4秒前 等 我不会为每个单独的计时器使用新的会话变量 而是创建一个Tr
  • 如何识别并设置 Maven 中缺少的环境属性?

    我进行了构建设置 以便通过命令行传递变量 mvn clean install DsomeVariable data 在我的 pom 中我有
  • 如何在Python中将有符号整数转换为无符号整数

    假设我有这个号码i 6884376 我如何将其称为无符号变量 就像是 unsigned long i in C Assuming 你心里有 2 的补码表示 和 By unsigned long you mean无符号 32 位整数 那么你只
  • 如何为Web方法传递可选参数?

    我有一个带有多个参数的网络方法 web方法只依赖2个字段 其余都是可选的 OperationContract public string WarehouseContactInformation int WAID Required strin
  • (一般)从自定义数据类型构建解析器?

    我正在开发一个需要与服务器通信的网络流媒体客户端 服务器将响应编码为字节串 例如 1 NULJohn NULTeddy NUL501 NUL 其中 NUL 是分隔符 上面的响应翻译为 这是一条类型1的消息 由服务器硬编码 它告诉客户端用户的