我尝试在 Ocaml 中实现这种 OO 情况:
两类X1
和 X2
, 两个子类型 X
( X1 <: X
和 X2 <: X
),我想编写一个动态返回 X
的函数要么是 X1
或 X2
.
但是我听说在 Ocaml 中避免使用类并使用模块通常会很好,所以我试图像这样表示我的问题(过于简化但仍然很重要):
两个模块X1
和 X2
,并且我希望我的函数能够动态决定是否返回 X1.t
或 X2.t
.
module type X = sig
type choice
type t
(* some methods we don't care about in this instance, like
val modifySomething : t -> t *)
end
module Xbase = struct
type choice = Smth | SmthElse
end
module X1 = (
struct
include Xbase
type t = { foo : int; bar : int }
end : X)
module X2 = (
struct
include Xbase
type t = { foo : int; star : int }
end : X)
module XStatic =
struct
(* construct either an X1.t or X2.t from the string *)
let read : string -> 'a =
function
| "X1" -> { X1.foo = 0, bar = 0 }
| "X2" -> { X2.foo = 1, star = 1 }
end
但这失败了 Error: Unbound record field label X1.foo
在 read
功能。
我尝试了不同的排列方式,例如使用 let open X1 in { foo = 0, ... }
但无济于事。
我解决这个问题的方法是根本错误的(即我应该使用类,因为这对模块来说是不可能/不切实际的)还是我只是错过了一些微不足道的东西?
编辑:澄清我要解决的问题并重命名module X
至module XBase
将其与 module type X
区分开来.
最佳答案
最简单的方法是使用 sum 类型(免责声明:我没有尝试编译代码):
module X1 = struct
type t = { foo : int; bar : string }
let modify_foo = ...
end
module X2 = struct
type t = { foo : int; star : bool }
let modify_foo = ...
end
type x1_or_x2 =
| Left of X1.t
| Right of X2.t
let read = function
| "X1" -> Left { X1.foo = 1; bar = "bar" }
| "X2" -> Right { X2.foo = 1; star = true }
let modify_foo = function
| Left x1 -> Left (X1.modify_foo x1)
| Right x2 -> Right (X2.modify_foo x2)
如果您想利用 X1.t
和 X2.t
共享一些共同结构这一事实,您可以分解类型。这个想法是它们与产品类型同构,分别是 common_part * specific_to_x1
和 common_part * specific_to_x2
。 x1_or_x2
类型因此是 (common * specific_to_x1) + (common * specific_to_x2)
,相当于 common * (specific_to_x1 + specific_to_x2)
。
type common = { foo : int }
let modify_foo_common : common -> common = ...
type specific_x1 = { bar : string }
type specific_x2 = { star : bool }
type x1_or_x2 = common * specific_x1_or_x2
and specific_x1_or_x2 =
| Left of X1.t
| Right of X2.t
let read = function
| "X1" -> { foo = 1 }, Left { bar = "bar" }
| "X2" -> { foo = 1 }, Right { star = true }
let modify_foo (common, specific) = (modify_foo_common common, specific)
这样,作用于公共(public)部分的定义不会重复,而是可以声明一次。
PS:另请参阅这个非常相关的问题,您可能对此感兴趣并且有一个很好的答案(镜头!):Ptival: Statically “extend” a record-ish data type without indirection hassle
关于ocaml - 动态创建两种记录类型之一的函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13798358/