ocaml - 摆脱变体构造函数
问题描述
作为一个附带项目,我尝试在 OCaml 中实现 RDF 库的基础知识。
您可能(或可能不)知道,RDF 语句(或三元组)由 3 个部分组成:
- 主题可以是 IRI 或空白节点;
- 谓词必须是 IRI ;
- 对象可以是 IRI、空白节点或文字。
我有 IRI、空白节点和文字的模块和类型,为了对上述规则进行类型验证,这是我开始编写的内容:
(* In `triple.ml` *)
type subject = Iri of Iri.t | Bnode of Bnode.t
type objekt = Iri of Iri.t | Bnode of Bnode.t | Literal of Literal.t
type t = subject * Iri.t * objekt
let create s p o = s, p, o
所以这很好,一切都很好,但有一件事让我感到厌烦:每当我想使用 时Triple.create
,我必须明确声明变体的构造函数:
let iri = (* Some Iri.t value *) in
let literal = (* Literal.t value *) in
Triple.create (Iri iri) iri (Literal literal)
我很确定 OCaml 有办法解决这个问题,但我不确定如何。
一些想法:我可以Triple.t
用它的主题类型和它的对象类型来参数化类型,但是我如何强制对参数类型进行限制?或者它可能是 GADT 的一个很好的用例?
解决方案
我不确定即使使用 GADT,您如何才能完全实现这一目标。在这种情况下将是什么类型create
?第一个参数必须是Iri.t
或者Bnode.t
除非一个是另一个的子类型,否则您不能编写这样的函数(否则它将非常通用:)'a -> ...
。
在任何情况下,您都需要提供一种类型的参数。您可以使用 GADT 将有关类型的信息“移动”到另一种类型中:
type 'a rdf_ty = II : (Iri.t * Iri.t) rdf_ty |
BI : (Bnode.t * Iri.t) rdf_ty |
IB : (Iri.t * Bnode.t) rdf_ty |
BB : (Bnode.t * Bnode.t) rdf_ty |
IL : (Iri.t * Literal.t) rdf_ty |
BL : (Bnode.t * Literal.t) rdf_ty
rdf_ty
编码 的第一个和第三个参数的类型create
:
type t = subject * Iri.t * objekt
let create : type a b. (a * b) rdf_ty -> a -> Iri.t -> b -> t = fun ty s p o ->
match ty with
| II -> Iri s, p, Iri o
| BI -> Bnode s, p, Iri o
| IB -> Iri s, p, Bnode o
| BB -> Bnode s, p, Bnode o
| IL -> Iri s, p, Literal o
| BL -> Bnode s, p, Literal o
let iri = (* Some Iri.t value *) in
let literal = (* Literal.t value *) in
create IL iri iri literal
但我真的怀疑这是一个比原版更好的版本。
推荐阅读
- c# - 初始化组件后调用方法
- react-native - 模态不出现
- vue.js - 带有 Vue CLI 3 的项目的导入路径上的 WebStorm 中的 Intellisense(代码完成辅助)不适用于 Vue.js
- java - 带有嵌入字段的列名无效
- angular - 使用 ngFor 和 ngModel 创建表格
- mongodb - 在 mongoDB 集合中存储巨大的 JSON 数组是否明智
- java - 如何在 Bpel 中调用 jsp 页面作为客户端合作伙伴链接
- mysql - MySQL 复制失败(从站未从主站获取数据)
- python - 更有效地实现 Textacy / spacy 'subject_verb_object_triples'
- ios - react-native-navigation 在 iOS 设备上倒退