gpt4 book ai didi

模块和记录字段

转载 作者:行者123 更新时间:2023-12-02 08:31:51 25 4
gpt4 key购买 nike

我偶然发现了一个相当简单的 OCaml 问题,但我似乎找不到一个优雅的解决方案。我正在使用应用于相对简单的模块的仿函数(它们通常定义一个类型和该类型的一些函数),并通过添加额外的更复杂的函数、类型和模块来扩展这些简单的模块。一个简化的版本是:

module type SIMPLE = sig
type t
val to_string : t -> string
val of_string : string -> t
end

module Complex = functor (S:SIMPLE) -> struct
include S
let write db id t = db # write id (S.to_string t)
let read db id = db # read id |> BatOption.map S.of_string
end

不需要给简单模块命名,因为它的所有功能都存在于扩展模块中,并且简单模块中的函数是由 camlp4 根据类型生成的。这些仿函数的惯用用法是:

module Int = Complex(struct
type t = int
end)

当我处理记录时出现问题:

module Point2D = Complex(struct
type t = { x : int ; y : int }
end)

let (Some location) = Point2D.read db "location"

似乎没有简单的方法可以从 Point2D 模块外部访问上面定义的 xy 字段,例如 location.xlocation.Point2D.x。我怎样才能实现这个目标?

编辑:根据要求,这是显示问题的完整最小示例:

module type TYPE = sig
type t
val default : t
end

module Make = functor(Arg : TYPE) -> struct
include Arg
let get = function None -> default | Some x -> (x : t)
end

module Made = Make(struct
type t = {a : int}
let default = { a = 0 } (* <-- Generated by camlp4 based on type t above *)
end)

let _ = (Made.get None).a (* <-- ERROR *)

最佳答案

让我们看一下所涉及的一些模块的签名。这些是Ocaml生成的签名,它们是主要签名,即它们是理论允许的最通用的签名。

module Make : functor (Arg : TYPE) -> sig
type t = Arg.t
val default : t
val get : t option -> t
end
module Made : sig
type t
val default : t
val get : t option -> t
end

请注意等式 Make(A).t = A.t 是如何保留的(因此 Make(A).t 是透明类型缩写),但 Made.t 是抽象的。这是因为 Made 是将仿函数应用于匿名结构的结果,因此在这种情况下,参数类型没有规范名称。

记录类型是生成的。在底层类型理论的层面上,所有生成类型的行为都类似于抽象类型,并带有一些构造函数和析构函数的语法糖。指定生成类型的唯一方法是给出其名称,可以是原始名称,也可以是通过一系列类型方程扩展为原始名称的名称。

考虑一下如果重复 Made 的定义会发生什么:

module Made1 = Make(struct
type t = {a : int}
let default = { a = 0 } (* <-- Generated by camlp4 based on type t above *)
end)
module Made2 = Make(struct
type t = {a : int}
let default = { a = 0 } (* <-- Generated by camlp4 based on type t above *)
end)

即使定义的右侧相同,您也会得到两种不同的类型 Made1.tMade2.t。这就是生成性的意义所在。

由于 Made.t 是抽象的,因此它不是记录类型。它没有任何构造函数。当结构参数关闭时,由于缺少名称,构造函数会丢失。

碰巧的是,对于记录,人们常常想要语法糖,而不是生成性。但 Ocaml 没有任何结构记录类型。它具有生成记录类型,并且具有对象,从类型理论的角度来看,对象包含记录,但实际上使用起来可能需要更多工作,并且性能损失较小。

module Made_object = Make(struct
type t = <a : int>
let default = object method a = 0 end
end)

或者,如果您想保持相同的类型定义,则需要为该类型及其构造函数提供一个名称,这意味着为该结构命名。

module A = struct
type t = {a : int}
let default = { a = 0 } (* <-- Generated by camlp4 based on type t above *)
end
module MadeA = Make(A)

请注意,如果构建 Make(A) 两次,则会得到相同的类型。

module MadeA1 = Make(A)
module MadeA2 = Make(A)

(好吧,这在这里并不引人注目,但您仍然会在 MadeA1MakeA2 中获得相同的抽象类型,这与上面的 Made1Made2 情况。这是因为现在这些类型有了一个名称:MadeA1.t = Make(A).t。)

关于模块和记录字段,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6759419/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com