我有这个OO情况,我试图在Ocaml中实现:两个类,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
Run Code Online (Sandbox Code Playgroud)
但这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)
Run Code Online (Sandbox Code Playgroud)
如果您想利用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)
Run Code Online (Sandbox Code Playgroud)
这样,作用于公共部分的定义不会重复,但可以声明一次.
PS:也看到这个非常相关的问题,你可能会感兴趣,哪个有一个很好的答案(镜头!):Ptival:静态"扩展"记录数据类型没有间接麻烦