2015-03-03 45 views
0

使用Signature/Functor模式,我指的是OCaml标准库中的Map.S/Map.Make的样式。这种模式非常成功,当你希望通过某种类型参数化一大段代码而不使其具有完全多态性时。基本上,您通过提供签名(通常称为S)和构造函数(Make)来引入参数化模块。Signature/Functor模式中的非抽象类型冗余

然而,当你仔细看看,有在声明中大量的冗余:

  • 首先,无论是签名和函子必须在.mli文件
  • 二将公布,签名必须在.ml文件中完全重复(实际上是否存在与此处的.mli文件不同的任何合法方式?)
  • 最后,函子本身必须再次重复所有定义才能实际实现模块类型

Summa summarum,我得到3个非抽象类型的定义网站(例如当我想允许模式匹配)。这是完全荒谬的,因此我认为有一些解决办法。所以我的问题是双重的:

  1. 有没有办法从.mli文件重复模块类型在.ml文件中,而不必手动写入?例如。像ppx_import模块签名?
  2. 有没有一种方法可以将模块类型包含在.ml文件中的模块中?例如。当模块类型只有一个抽象类型定义时,定义该类型并仅复制非抽象类型?

回答

1
  • 您可以将ppx_import用于模块签名。你甚至可以在.ml中使用它来查询相应的.mli文件。
  • 如果一个模块仅由模块签名组成,您可以单独定义.mli,不带任何.ml。这样,您可以定义一个模块,比如说Foo_sigs,包含签名并在其他地方使用。
+0

我最近尝试了第二个选项,但未能使它与ocamlbuild一起构建。你能发布一个用ocamlbuild构建的最小工作示例吗? – user3240588 2015-03-03 10:37:19

+1

您能否提供第一个选项的示例?我如何从.mli ppx_import签名?第一个选项 – choeger 2015-03-03 11:00:34

+0

,camlspotter回答。 – Drup 2015-03-03 14:31:21

1

重复类型和模块类型定义可以避免将它们移动到外部.ml文件。让我们来看看下面的例子:

module M : sig 

    (* m.mli *)  
    module type S = sig 
    type t 
    val x : t 
    end 

    module type Result = sig 
    type t 
    val xs : t list 
    end 

    module Make(A : S) : Result with type t = A.t 

end = struct 

    (* m.ml *) 
    module type S = sig 
    type t 
    val x : t 
    end 

    module type Result = sig 
    type t 
    val xs : t list 
    end 

    module Make(A : S) = struct 
    type t = A.t 
    let xs = [A.x;A.x] 
    end 

end 

而不是写两个文件m.mlim.ml的,我用了一个模块M有一个明确的签名:这相当于有两个文件,您可以通过复制试穿OCaml的顶层 - 和粘贴。

M,东西被骗了sig .. endstruct .. end。如果模块类型变大,这很麻烦。

你可以通过将它们移动到另一个.ml文件来共享这些软件。例如,像下面n_intf.ml

module N_intf = struct 

    (* n_intf.ml *)  
    module type S = sig 
    type t 
    val x : t 
    end 

    module type Result = sig 
    type t 
    val xs : t list 
    end 

end 

module N : sig 

    (* n.mli *) 
    open N_intf 
    module Make(A : S) : Result with type t = A.t 

end = struct 

    (* n.ml *) 
    open N_intf 

    module Make(A : S) = struct 
    type t = A.t 
    let xs = [A.x;A.x] 
    end 

end 

您还可以使用*_intf.mli代替*_intf.ml,但我建议使用*_intf.ml,因为:

  • 模块包装不采取mli只有模块进去,因此你安装时必须复制*_intf.cmi
  • 从类型定义(如ppx_deriving)生成代码需要在.ml中定义的东西。在这个例子中,没有这种情况,因为没有类型定义。
0

在这种特定的情况下,你可以跳过.mli部分:

  • 你的抽象由.ml
  • 读它使规定相当清楚(因为人们知道,从模式在STDLIB)
  • 一切,你会放在.mli已经在.ml

如果多数民众赞成需要真正给时许在一组工作李,只需使用ocamlc -i技巧自动生成。

ocamlc -i m.ml >m.mli # automatically generate mli from ml 

我知道它并不完全回答你的问题,但嘿,它解决了你的问题。

我知道总是将mli视为最佳做法,但这不是强制性的,可能出于某些非常好的原因。

关于你的第二个问题,我不知道我的理解是很好,但我觉得这回答它:

module type ToCopy = sig type t val f : t -> unit end 
module type Copy1 = sig include ToCopy with type t = int end 
module type Copy2 = ToCopy with type t = int;; 
+0

假设我在同一个.ml文件中有其他值,我想限制它的签名。我不能只将它们放在.mli中,而忽略.mli中有问题的模块M的签名,因为这会将M隐藏到外部,对吗?那么在这种情况下,解决方案看起来并不理想? – user3240588 2015-03-03 13:18:01

+0

正确的,你可以将它分成两个不同的文件(或者通过'-i'转储获得你想要的mli的一部分)。我同意避免使用一些冗余,但在大多数情况下可以通过简单的解决方法来处理它。 – PatJ 2015-03-03 13:24:05

0

添加到camlspoter的答案,因为这个问题提到了模式匹配,也许你想“重新导出“在N_intf中声明的构造函数的签名和类型,以便通过N来访问它们。在这种情况下,你可以用includemodule type of,更换open的,即:

module N_intf = struct 

    type t = One | Two 

    (* n_intf.ml *)  
    module type S = sig 
    type t 
    val x : t 
    end 

    module type Result = sig 
    type t 
    val xs : t list 
    end 

end 

module N : sig 

    (* n.mli *) 
    include module type of N_intf 

    module Make(A : S) : Result with type t = A.t 

end = struct 

    (* n.ml *) 
    include N_intf 

    module Make(A : S) = struct 
    type t = A.t 
    let xs = [A.x;A.x] 
    end 

end 

然后你会得到以下特征:

module N_intf : 
    sig 
    type t = One | Two 
    module type S = sig type t val x : t end 
    module type Result = sig type t val xs : t list end 
    end 

module N : 
    sig   
    type t = One | Two 
    module type S = sig type t val x : t end 
    module type Result = sig type t val xs : t list end 
    module Make : functor (A : S) -> sig type t = A.t val xs : t list end 
    end 

现在的建设者OneTwo可以胜任通过N而不是N_intf,因此您可以在程序的其余部分忽略N_intf