使用显式类型将对象中方法的可见性限制为定义它的模块

时间:2017-11-15 18:30:01

标签: types ocaml

我有一个坐标" class" (在OCaml中使用返回对象文字的工厂函数实现)并且我试图弄清楚如何将该对象上方法的可见性限制为它所定义的模块。

这是源文件

(* coordinate.ml *)
type coordinate = < get_x : int; get_y : int >

let create_coord ~x ~y = object
  val x = x
  val y = y
  method get_x = x
  method get_y = y
  (* hide this method from public interface *)
  method debug_print = Printf.printf "(x=%d, y=%d)\n"
end

这是界面

(* coordinate.mli *)
(* the type synonym coordinate is compatible with the "real"
 * type, but does not expose the debug_print method *)
type coordinate = < get_x : int; get_y : int >

val create_coord : x:int -> y:int -> coordinate

我的目的是允许create_coord返回的对象在Coordinate模块的主体内部显示所有方法。在coordinate文件中重复.ml类型别名的唯一原因是为了满足编译器/允许它在.mli中使用。

但是,我想阻止此模块的使用者使用debug_print方法。我认为,&#34;因为OCaml支持结构类型&#34;,具有严格较少方法的对象类型将是&#34;兼容的&#34;类型为类型归属。

但是,当我尝试编译文件时,我收到以下错误:

$ ocamlc coordinate.mli
$ ocamlc coordinate.ml
File "coordinate.ml", line 1:
Error: The implementation coordinate.ml
       does not match the interface coordinate.cmi:
       Values do not match:
         val create_coord :
           x:'a ->
           y:'b ->
           < debug_print : int -> int -> unit; get_x : 'a; get_y : 'b >
       is not included in
         val create_coord : x:int -> y:int -> coordinate
       File "coordinate.ml", line 3, characters 4-16: Actual declaration
Exit 2

有没有办法限制debug_print Coordinatestring = "A" secret_word = "Apple" if string in secret_word: print("Good!") else: print("Bad...") 之外的可见度,同时让它在内部可以自由访问?

3 个答案:

答案 0 :(得分:4)

我认为最好的解决方案是使用私有行类型:

module M : sig
  type t = private < x : int; y : int; .. >
  val make : int -> int -> t
  val f: t -> unit
end = struct
  type t = < x : int; y : int; z : [`Not_exposed] >

  let make x y = object
    method x = x
    method y = y
    method z = `Not_exposed
  end

  let f o =
    (* Access a method not exposed outside of the module *)
    assert (o#z = `Not_exposed)
end;;

这样,您不需要任何强制功能。尽管如此,任何类型为t的对象都可以在模块M中看到他的私有方法,但只能在外部访问公共方法(xy)。在manual

中查找更多信息

答案 1 :(得分:2)

你可以通过明确的强制来完成一些工作。一个最小的例子:

../configure CC=icc CXX=icpc FC=ifort CFLAGS="-D_Float128=__float128"

仍然隐藏了type t = < x : int; y : int > let make x y : t = let o = object method x = x method y = y method z = `Not_exposed end in (o :> t) (* val make : int -> int -> t = <fun> *) 内部模块。您可以通过隐藏z

来进一步了解
make

但是这不允许您将module M : sig type t = < x : int; y : int > val make : int -> int -> t end = struct type t = < x : int; y : int > let make x y = object method x = x method y = y method z = `Not_exposed end let f o = (* Access a method not exposed outside of the module *) assert (o#z = `Not_exposed) (* Shadow the make function, coercing the result into type t *) let make x y = (make x y :> t) end 公开为f,因为t -> unit没有t方法。

您还可以公开两种类型,将完整对象类型抽象保留在模块的上下文之外,并提供强制/转换为公开的,更受限制的类型的函数:

z

答案 2 :(得分:1)

对于后代,我发现可以使用显式的多态类型和显式的向上转换来使OCaml确信可以“忘记”一种方法。

type kata = < foo : int; bar : string >
type ana = < foo : int >

let to_ana (k : < foo : int ; .. >) : ana = (k :> ana)

这与多态代码一起很好地工作。

to_ana (object method foo = 42 method bar = "asdf" method baz = [] end)