首页 > 解决方案 > 摆脱变体构造函数

问题描述

作为一个附带项目,我尝试在 OCaml 中实现 RDF 库的基础知识。

您可能(或可能不)知道,RDF 语句(或三元组)由 3 个部分组成:

我有 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 的一个很好的用例?

标签: ocamlvariant

解决方案


我不确定即使使用 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

但我真的怀疑这是一个比原版更好的版本。


推荐阅读