【问题标题】:make variant constructors public in .mli在 .mli 中公开变体构造函数
【发布时间】:2019-01-30 05:14:54
【问题描述】:

考虑something.ml

type some_type =
  | This
  | That

然后,我可以像这样实现main.ml

let x = Something.This

我想创建something.mli 并在main.ml 中保留相同的功能。我的第一次尝试是将something.mli 写为:

type some_type

我认为这会使变体构造函数公开可用,但它没有,现在main.ml 无法编译。有没有办法在.mli 中公开变体构造函数?

【问题讨论】:

    标签: ocaml


    【解决方案1】:

    something.mli 文件提供了 something.ml 文件的接口。因此,您希望在界面中可见的任何内容都必须在 something.mli 中定义。

    由于您希望 ThisThat 可见,因此必须在 something.mli 中定义它们。

    对于您的小示例,something.mli 将包含您在上面为 something.ml 显示的内容:

    type some_type = This | That
    

    当然,在一个更现实的例子中,接口包含的内容要比实现少得多。特别是它只有公共函数的类型而不是代码。

    【讨论】:

      【解决方案2】:

      .mli 文件单独定义模块的接口,编译它们时根本不使用 .ml 文件。实际上,您可以为由多个 .ml 文件组成的包创建一个 .mli 文件。永远不会神奇地将 .ml 文件中的某些内容拉入界面。

      现在,与在 .ml 文件中一样,在 .ml 文件中指定类型有三种方式:

      1) 作为抽象类型。没有任何类型的暴露:

      # type some_type;;
      type some_type
      # let v = This;;   
      Error: Unbound constructor This
      # let to_int = function This -> 1 | That -> 2;;
      Error: Unbound constructor This
      

      这从外部隐藏了类型的详细信息,允许模块稍后随意更改类型而不会破坏任何源代码。它也用于没有值或外部值(参见手册中的 C 接口)的非 ocaml 类型的幻像类型。

      2) 作为公共类型。暴露了类型的结构,可以创建值:

      # type some_type = This | That;;
      type some_type = This | That
      # let v = This;;
      val v : some_type = This
      # let to_int = function This -> 1 | That -> 2;;
      val to_int : some_type -> int = <fun>
      

      这与第一种情况相反。一切都是公开的。

      但还有第三种选择:

      3) 作为私有类型。暴露了类型的结构,但无法创建值:

      # type some_type = private This | That;;
      type some_type = private This | That
      # let v = This;;
      Error: Cannot create values of the private type some_type
      # let to_int = function This -> 1 | That -> 2;;
      val to_int : some_type -> int = <fun>
      

      这在 1 和 2 之间。用例是当您需要控制值的构造时。例如,考虑一个包含小于 100 的小整数的类型。你可以这样写:

      # let make x =
            if x < 0 || x >= 100
                then raise (Invalid_argument "Out of range")
                else x;;
      val make : int -> int = <fun>
      

      然后将 .mli 文件编写为:

      type t = private int;;
      val make : int -> t;;
      

      这确保了 t 类型的值只能使用 make 函数来构造。任何期望类型 t 的东西都只会接受由 make 构造的类型 t 的值。另一方面,任何期望 int 类型的东西也将接受 t 类型的值。后者不会是抽象类型的情况。

      【讨论】:

      • 这是一个非常有用的答案——我个人完全忘记了private 变体。答案中没有提到,但值得注意的是,这样做的目的是允许消费者对变体类型进行模式匹配,而不允许构造它。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-04-22
      • 2022-01-16
      • 1970-01-01
      • 1970-01-01
      • 2011-03-01
      • 1970-01-01
      • 2013-08-29
      相关资源
      最近更新 更多