用于动态创建两种记录类型之一的函数

Dan*_*Dan 3 ocaml

我有这个OO情况,我试图在Ocaml中实现:两个类,X1并且X2,子类型X(X1 <: XX2 <: X),我想写一个动态返回X一个X1或将来的函数X2.

但是我听说它通常是很好的避免OCaml中类和使用的模块代替,所以我想代表我的问题是这样的(过于简化,但仍使点):两个模块X1X2,我想我的功能之间进行动态决定返回一个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.fooread功能中失败了.我已经尝试了不同的方式来安排它,比如使用let open X1 in { foo = 0, ... }但无济于事.

我的方法是否从根本上是错误的(即我应该使用类,因为这对模块来说是不可能/不实用的)或者我只是缺少一些微不足道的东西?

编辑:澄清我试图解决这个问题,并更名module Xmodule XBase从区分开来module type X.

gas*_*che 6

最简单的方法是使用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.tX2.t共享一些共同结构的事实,您可以对类型进行分解.这个想法是,它们分别是同构的产品类型,common_part * specific_to_x1common_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:静态"扩展"记录数据类型没有间接麻烦