摆脱变体构造函数

时间:2018-11-19 22:15:08

标签: ocaml variant

作为辅助项目,我尝试在OCaml中实现RDF库的基础。

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

  • 主题可以是IRI或空白节点;
  • 谓词必须是IRI;
  • 对象可以是IRI,空白节点或文字。

我有IRI,空白节点和文字的模块和类型,并且为了对上述规则进行类型校对,这是我开始写的内容:

g = df.groupby("difference_in_days")['total_ordered'].mean()

# value is the average total_ordered for rows with 56 days of difference.
value = g[g.index.days == 56].iloc[0] 

这一切都很好,但有一件事情使我感到烦恼:每当我想使用(* 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

我很确定OCaml可以解决此问题,但是我不确定如何解决。

一些想法:我可以用其主体的类型和其对象的类型来参数化let iri = (* Some Iri.t value *) in let literal = (* Literal.t value *) in Triple.create (Iri iri) iri (Literal literal) 类型,但是如何对参数类型施加限制呢?还是对于GADT来说是一个很好的用例?

2 个答案:

答案 0 :(得分:1)

我不确定即使使用GADT也不能完全实现这一目标。在这种情况下,create的类型是什么?除非第一个参数是Iri.tBnode.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_tycreate的第一个和第三个参数的类型进行编码:

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

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

答案 1 :(得分:1)

如果您不介意更改Iri.t的类型,可以执行以下操作(将internal = string替换为实际类型):

module Iri : sig
  type internal
  type t = [`Iri of internal]
  val v : string -> [> t]
end = struct
  type internal = string
  type t = [`Iri of internal]
  let v x = `Iri x
end

module Bnode : sig
  type internal
  type t = [`Bnode of internal]
  val v : string -> [> t]
end = struct
  type internal = string
  type t = [`Bnode of internal]
  let v x = `Bnode x
end

module Literal : sig
  type internal
  type t = [`Literal of internal]
  val v : string -> [> t]
end = struct
  type internal = string
  type t = [`Literal of internal]
  let v x = `Literal x
end

module Triple = struct
  type subject = [Iri.t | Bnode.t]
  type objekt = [Iri.t | Bnode.t | Literal.t]

  type t = subject * Iri.t * objekt

  let v s p o : t = s, p, o
end

let alice = Iri.v "alice"
let knows = Iri.v "knows"
let bob = Iri.v "bob"

let x1 = Bnode.v "blank-x1"

let foo = Literal.v "foo"

let triple1 = Triple.v alice knows bob
let triple2 = Triple.v bob knows x1
let triple3 = Triple.v bob knows foo

请注意,在最后的示例中,相同的值bob既用作主题([Iri.t | Bnode.t])又用作对象([Iri.t | Bnode.t | Literal.t])。