OCaml 使用定义在外部仿函数的签名来限制对生成模块的可见性

OCaml use signature defined outside functor to limit visibility into produced module

我正在尝试编写一个仿函数,它接受一对有序的事物并产生另一个有序的事物(按字典顺序定义排序)。

但是,我希望生成的 "ordered type" 是抽象的,而不是 OCaml 元组。

使用 inline/anonymous 签名很容易做到这一点。

(* orderedPairSetInlineSig.ml *)

module type ORDERED_TYPE = sig
  type t
  val compare : t -> t -> int
end

module MakeOrderedPairSet (X : ORDERED_TYPE) :
  sig
    type t
    val get_fst : t -> X.t
    val get_snd : t -> X.t
    val make : X.t -> X.t -> t
    val compare : t -> t -> int
  end = struct
    type t = X.t * X.t

    let combine_comparisons fst snd =
      if fst = 0 then snd else fst

    let compare (x, y) (a, b) =
      let cmp  = X.compare x a in
      let cmp' = X.compare y b in
      combine_comparisons cmp cmp'

    let get_fst ((x, y) : t) = x

    let get_snd ((x, y) : t) = y

    let make x y = (x, y)
  end

我想给我的匿名签名起一个像 ORDERED_PAIR_SET_TYPE 这样的名字,然后把它移到 MakeOrderedPairSet 的定义之外,像这样(警告:语法上无效):

(* orderedPairSet.ml *)

module type ORDERED_TYPE = sig
  type t
  val compare : t -> t -> int
end

module type ORDERED_PAIR_SET_TYPE = sig
  type t
  type el

  val get_fst : t -> el
  val get_snd : t -> el
  val make : el -> el -> t

  val compare : t -> t -> int
end

module MakeOrderedPairSet (X : ORDERED_TYPE) :
  (ORDERED_PAIR_SET_TYPE with type el = X.t) = struct
    type t = X.t * X.t

    let combine_comparisons fst snd =
      if fst = 0 then snd else fst

    let compare (x, y) (a, b) =
      let cmp  = X.compare x a in
      let cmp' = X.compare y b in
      combine_comparisons cmp cmp'

    let get_fst ((x, y) : t) = x

    let get_snd ((x, y) : t) = y

    let make x y = (x, y)
  end

其中 el 是签名中的抽象类型,我试图绑定到 MakeOrderedPairSet 正文中的 X.t

但是,我不知道如何将所有内容组合在一起。

(ORDERED_PAIR_SET_TYPE with type el = X.t) 是我能想到的最明显的表达方式 "give me a signature that's just like this one, but with an abstract type replaced with a concrete one (or differently-abstract in this case)"。但是,在这种情况下它在语法上是无效的(因为有括号)。去掉括号也不会导致有效的 "module-language-level expression";我保留它是因为我认为它使我的意图更加明显。

那么...您如何使用命名签名来限制对[仿函数产生的模块]/[参数化模块]的可见性?

如果你不想将el添加到模块的导出中,那么有两种方法:

  1. 使用替代约束:

    ORDERED_PAIR_SET_TYPE with type el := X.t
    

    这将从签名中删除 el 的规范。

  2. 使用参数化签名。不幸的是,这不能直接在 OCaml 中表达,但需要围绕签名的定义进行一些额外的仿函数操作:

    module SET_TYPE (X : ORDERED_TYPE) =
    struct
      module type S =
        sig
          type t
          val get_fst : t -> X.el
          val get_snd : t -> X.el
          val make : X.el -> X.el -> t
          val compare : t -> t -> int
        end
      end
    

    有了它你可以写:

    module MakeOrderedPairSet (X : ORDERED_TYPE) : SET_TYPE(X).S = ...