GADT 的解析和使用

2023-12-04

我在编写解析器时遇到了问题。具体来说,我想要不同类型的返回值。例如,我有两种不同的数据类型FA and PA代表两种不同的脂质类别 -

data FA = ClassLevelFA IntegerMass
        | FA           CarbonChain
        deriving (Show, Eq, Ord)

data PA   = ClassLevelPA       IntegerMass
          | CombinedRadylsPA   TwoCombinedRadyls
          | UnknownSnPA        Radyl Radyl
          | KnownSnPA          Radyl Radyl
          deriving (Show, Eq, Ord)

使用 attoparsec,我构建了解析器来解析脂质速记符号。对于上面的数据类型,我有解析器faParser and paParser。我希望能够解析FA or PA。然而,自从FA and PA是不同的数据类型,我不能执行以下操作 -

inputParser =  faParser
           <|> paParser

我最近了解了 GADT,我认为这可以解决我的问题。因此我做了一个 GADT 和一个eval函数并更改了解析器faParser and paParser. -

data ParsedLipid a where
  ParsedFA :: FA -> ParsedLipid FA
  ParsedPA :: PA -> ParsedLipid PA

eval :: ParsedLipid a -> a
eval (ParsedFA val) = val
eval (ParsedPA val) = val

这让我很接近,但看起来好像ParsedFA and ParsedPA是不同的数据类型吗?例如,解析"PA 17:1_18:1"给我一个类型的值ParsedLipid PA(不只是ParsedLipid正如我所期待的那样)。因此,解析器inputParser仍然不进行类型检查。

let lipid = use "PA 17:1_18:1"
*Main> :t lipid
lipid :: ParsedLipid PA

关于如何解决这个问题有什么建议吗?


@MathematicalOrchid 指出您可能不需要 GADT,简单的求和类型可能就足够了。您可能有一个XY问题但我对你的用例了解不够,无法做出坚定的判断。这个答案是关于如何将非类型化数据转换为 GADT。

正如您所注意到的,您的解析函数无法返回ParsedLipid a因为这使得调用上下文可以自由选择a这没有道理;a实际上是由输入数据决定的。并且你不能返回ParsedLipid FA or a ParsedLipid PA,因为输入数据可以是任一类型。

因此,当您事先不知道索引的类型时,从运行时数据构建 GADT 时的标准技巧是使用存在量化.

data AParsedLipid = forall a. AParsedLipid (ParsedLipid a)

类型参数a出现在右侧AParsedLipid但不在左边。值为AParsedLipid保证包含一个格式良好的ParsedLipid,但其精确类型是保密的。存在类型是一个包装器,它帮助我们从混乱的、无类型的现实世界转换为干净的、强类型的 GADT。

将存在量化的包装器推到系统的边缘是一个好主意,您必须在其中与外界进行通信。您可以编写带有签名的函数,例如ParsedLipid a -> a在你的核心模型中,并将它们应用到边缘的存在包装下的数据。您验证您的输入,将其包装在存在类型中,然后使用强类型模型安全地处理它 - 这不必担心错误,因为您已经检查了输入。

您可以解压一个AParsedLipid得到你的ParsedLipid返回,并对其进行模式匹配以确定运行时的内容a是 - 要么是FA or PA.

showFA :: FA -> String
showFA = ...
showPA :: PA -> String
showPA = ...

showLipid :: AParsedLipid -> String
showLipid (AParsedLipid (ParsedFA x)) = "AParsedLipid (ParsedFA "++ showFA x ++")"
showLipid (AParsedLipid (ParsedPA x)) = "AParsedLipid (ParsedPA "++ showPA x ++")"

你会注意到a不能出现在采用 a 的函数的返回类型中AParsedLipid要么出于上述原因。返回类型必须为编译器所知;该技术不允许您定义“具有不同返回类型的函数”。

当你构建一个AParsedLipid,你必须生成足够的证据来说服编译器包装的ParsedLipid是格式良好的。在你的例子中,这归结为解析一个类型良好的PA or FA然后把它包起来。

parser :: Parser AParsedLipid
parser = AParsedLipid <$> (fmap ParsedFA faParser <|> fmap ParsedPA paParser)

与运行时数据一起使用时,GADT 有点尴尬。存在包装器有效地删除了额外的编译时信息ParsedLipid - AParsedLipid同构于Either FA PA。 (证明代码中的同构是一个很好的练习。)根据我的经验,GADT 在结构化方面要好得多programs而非构建data- 他们擅长实现强类型嵌入式领域特定语言,这些语言的类型索引可以在编译时得知。例如,Yampa and 可扩展效果两者都使用 GADT 作为其中心数据类型。这有助于编译器检查您在编写的代码中是否正确使用了特定于域的语言(并且在某些情况下允许某些优化)。您不太可能在运行时根据现实世界的数据构建 FRP 网络或单子效应。

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

GADT 的解析和使用 的相关文章

  • Haskell 中的实例声明

    我有这两个功能 primes sieve 2 where sieve p xs p sieve x x lt xs x mod p gt 0 isPrime number number 1 null x x lt takeWhile x g
  • 在 Haskell 中增长数组

    我想在 Haskell 中实现以下 命令式 算法 给定一个序列对 e0 s0 e1 s1 e2 s2 en sn 其中 e 和 s 部分不一定是自然数不同的是 在每个时间步都会随机选择该序列的一个元素 例如 ei si 并根据 ei si
  • Haskell:是的,没有类型类。为什么是整数?

    我有一个关于 GHCi 如何假定整数类型的问题 我正在阅读 Learn you a Haskell 是 否类型的课程 如果您想阅读全文 这里有一个链接 http learnyouahaskell com making our own typ
  • Haskell - 用防护罩替换外壳

    我想知道在这部分代码中是否可以用守卫替换 case 语句 firstFunction String gt Maybe MyType secondFunction MyType gt Integer myFunction String gt
  • 如何在 Haskell 中安装库?

    我尝试使用控制 Monad Extra andM https hackage haskell org package extra 1 7 10 docs Control Monad Extra html import Control Mon
  • Haskell 入门

    这个问题的答案是社区努力 help privileges edit community wiki 编辑现有答案以改进这篇文章 目前不接受新的答案或互动 几天来 我一直试图理解 Haskell 中的函数式编程范例 我通过阅读教程和观看截屏视频
  • Haskell / GHC - 是否有“警告不完整模式”的中缀标签/编译指示

    我正在寻找一个可以对特定的不完整模式发出警告的编译指示 它会使编译器失败并显示以下 假设的 代码 FAILIF incomplete patterns f Int gt Int f 0 0 我正在尝试使用 Arrows 编写一个 编译器 并
  • Traversable 类型类的用途

    有人可以向我解释一下类型类的目的是什么吗Traversable 类型类定义是 class Functor t Foldable t gt Traversable t gt where So Traversable is a Functor
  • 找不到模块“Yesod”

    我有以下代码 LANGUAGE TypeFamilies QuasiQuotes MultiParamTypeClasses TemplateHaskell OverloadedStrings module Simple where imp
  • Haskell:IORef 的性能

    我一直在尝试在 Haskell 中编码一个需要使用大量可变引用的算法 但与纯粹的惰性代码相比 它 也许并不奇怪 非常慢 考虑一个非常简单的例子 module Main where import Data IORef import Contr
  • 这个对自身单位的列表理解是如何工作的?

    在 haskell IRC 频道中有人问 是否有一种简洁的方法来定义一个列表 其中第 n 个条目是之前所有条目的平方和 我认为这听起来像一个有趣的谜题 递归定义无限列表是我真正需要练习的事情之一 所以我启动了 GHCi 并开始尝试递归定义
  • Haskell 标准库是什么?

    GHC专用库可以称为标准库吗 或者只有 Haskell 2010 报告中的那些才算数 许多 GHC 库可以通过 Haskell 报告中的函数来实现 可能与 C 绑定相结合 但其他语言依赖于 GHC 特定的扩展 因为语言报告中定义的当前 Ha
  • Data.Sequence 中的 inits 和 tails 如何工作?

    Louis Wasserman 编写了当前的实现inits and tails in Data Sequence 他表示它们非常高效 事实上 只要查看代码 我就可以看到 无论它们在做什么 它们都是以干净 自上而下的方式进行的 这往往会给惰性
  • 如何在不声明新数据的情况下更改类型(String,Int)元组的 Ord 实例?

    我正在尝试对类型列表进行排序 String Int 默认情况下 它按字符串排序 然后按整数排序 如果字符串相等 我希望它是相反的 首先比较整数 然后如果相等则比较字符串 另外 我不想切换到 Int String 我找到了一种通过定义实例来实
  • 如何更换HXT中的节点?

    给定一个示例 xml 文件
  • 约束包如何工作?

    背后的想法数据 约束 Forall http hackage haskell org packages archive constraints 0 3 2 doc html src Data Constraint Forall html据我
  • Haskell 处理负参数

    尝试对两个值求和 其中只有一个为负值 例如 1 and 2 soma Float gt Float gt Float soma x1 x2 x1 x2 结果出现错误 为什么
  • 树莓派 2 上的 GHCi?

    我正在开发一些在 raspberry pi 2 上运行的 haskell 项目 以及可以使用 raspbian 7 4 1 中的 apt get 安装的 ghc 版本 但它没有 GHCi 这会阻止一些重要的包 如 Vector 的编译 我看
  • Haskell:对 Num 类型类的使用感到困惑

    我很困惑为什么这有效 f Num a gt a gt a f x x 42 但这并没有 g Num a gt a gt a g x x 4 2 我本来就明白Num包含实现运算符的所有类型 因此 如果42 is an Int and 4 2
  • 在 ghci 下执行 `(read "[Red]") :: [Color]` 时会发生什么?

    我正在阅读以下小节现实世界 Haskell 第 6 章 类型类 http book realworldhaskell org read using typeclasses html关于一个实例Read for Color 它实现了reads

随机推荐

  • 当MySql数据库中添加新记录时,使用Jquery自动更新Div

    我正在为我的朋友制作一个社交网站 我想知道当数据库中添加新记录时 如何更新包含数据库中少量插入记录的 Div 简而言之 你一定见过 Facebook 的实时通知 当有人做某事时 这些通知就会淡出 这一切都发生在没有刷新整个实时通知 div
  • 以编程方式检查 SD 卡是否可用

    我的应用程序适用于仅具有 SD 卡的手机 因此 我想以编程方式检查 SD 卡是否可用以及如何找到 SD 卡可用空间 是否可以 如果是 我该怎么做 Boolean isSDPresent android os Environment getE
  • 在 Windows 中托管 Git 存储库

    目前是否有办法在 Windows 中托管共享 Git 存储库 据我所知 您可以使用以下命令在 Linux 中配置 Git 服务 git daemon 是否有本地 Windows 选项 缺少共享文件夹 来托管 Git 服务 编辑 我目前正在使
  • 创建两列无​​序列表

    我希望制作一个类似于下面的两列无序列表 但也想知道如何整合加号的图像 我正在寻找一种没有 CSS3 优点的解决方案 以便它可以支持较旧的浏览器 这是我的网站的链接 http jobspark ca job listings 由于我正在使用
  • magento 的 NGINX-FPM 配置设置

    我正在运行一个用 magento 开发的电子商务网站 我的服务器有 512mb RAM 和 2 6 core2duo 当我在网站上一次发送 50 个请求时 除了少数请求外 它不会响应 我也安装了清漆 我想知道我想要为我的网站进行的最佳设置
  • 变异,触发器/函数可能看不到它 - 触发器执行期间出错

    CREATE OR REPLACE TRIGGER UPDATE TEST 280510 AFTER insert on TEST TRNCOMPVISIT declare V TRNCOMPNO NUMBER 10 CURSOR C1 I
  • 使用 Findbugs 编写一个检测器来搜索“System.out.println”的使用

    我正在尝试编写一个错误检测器来使用 Findbugs 查找方法调用 System out println 的实例 我知道字节码中的 System out println 被编译为对 GETSTATIC 的调用 将 System out 推入
  • android - 活动切换时意外的短暂方向变化

    我想在我的 Android 应用程序中动态设置屏幕方向 为此我使用 activity setRequestedOrientation ActivityInfo SCREEN ORIENTATION PORTRAIT 和类似的 到目前为止 效
  • 定义 Vue-Router 路由时访问 Vuex 状态

    我有以下 Vuex 商店 main js import Vue from vue import Vuex from vuex Vue use Vuex init store const store new Vuex Store state
  • Jetty 和最大内容大小

    我使用 Jetty 9 4 8 我想限制可以发布到服务器的数据量 为此 我添加到 jetty xml
  • CUDA 应用程序在几秒钟后超时并失败 - 如何解决此问题?

    我注意到 CUDA 应用程序在失败并退出之前的最大运行时间往往为 5 15 秒 我意识到最好不要让 CUDA 应用程序运行那么长时间 但假设使用 CUDA 是正确的选择 并且由于每个线程的顺序工作量必须运行那么长时间 有什么方法可以延长这个
  • VBA复制列的宽度

    下面的 VBA 代码从源数据表复制数据并将其粘贴到特定表上 但是 我还需要它来将列的宽度粘贴到源数据表上 这可能吗 谢谢你的帮助 Private Sub Worksheet Change ByVal Target As Range Dim
  • 创建进程以在新的 Windows 桌面上运行 IE

    我正在尝试设置一个 IE kiosk 在单独的桌面上运行 IE 当我测试时 我只是正常启动 IE 不是在 kiosk 模式下 但是尽管 IE 在新桌面上启动 但它不会加载命令字符串中指定的初始页面 它只是坐在那里 沙漏闪烁打开和关闭非常快
  • 无法编译 C# 默认接口方法

    C 8 0 有一个新功能 可以让您向接口上的方法添加默认实现 要么我做错了什么 要么这个功能没有像宣传的那样工作 我猜是前者 我使用以下代码创建了一个新的 NET Core 3 1 控制台应用程序 using System namespac
  • JQuery .append 标记被忽略[重复]

    这个问题在这里已经有答案了 我有以下 HTML div div
  • 使用外键作为第一个表的标识列批量插入嵌套 xml

    我有一个 xml 如下
  • 使用提交按钮时,ajax 调用不起作用

    我正在尝试使用以下 API 获取实时货币汇率 http www exchangerate api com INR USD 1 k FQRxs xT2tk NExQj 当我点击按钮时 它会提醒费率并且工作正常 我正在使用以下 Ajax 代码
  • 仅将 AngularJS 用于路由目的

    我刚刚被分配了一个完全用 jQuery 制作的网站 它异步加载几个页面 并且想成为一个SPA 现在唯一的问题是开发人员没有考虑一般的 URL 人们无法通过 www example com 以外的任何其他方式访问该网站 我对 AngularJ
  • std::move(const shared_ptr 引用) 是什么意思?

    以下是我正在尝试的玩具代码 我理解第一个和第二个 第一个将所有权授予 p 第二份副本p to p 但我不明白第三个 什么是std move of const shared ptr 意思是 谢谢 class P class A public
  • GADT 的解析和使用

    我在编写解析器时遇到了问题 具体来说 我想要不同类型的返回值 例如 我有两种不同的数据类型FA and PA代表两种不同的脂质类别 data FA ClassLevelFA IntegerMass FA CarbonChain derivi