作为辅助项目,我尝试在OCaml中实现RDF库的基础。
您可能(也可能不知道)知道,RDF语句(或三元组)由3部分组成:
我有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来说是一个很好的用例?
答案 0 :(得分:1)
我不确定即使使用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
但是我真的怀疑这是一个比原始版本更好的版本。
答案 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]
)。