OCaml中类型和模块相等的规则是什么

Ale*_* B. 2 ocaml

我无法理解OCaml中模块的相等性.函数应该是应用程序(这是互联网所声称的),但这似乎有时会失败,我不能完全看到它背后的一般规则.

这是我的示例代码:

module type PT = sig end
module P = struct end

let () =
  Random.self_init ()

module OrigHashtbl = Hashtbl
module Hashtbl = struct
  module Make(Hash: OrigHashtbl.HashedType) = struct
    let random = Random.int 1000000

    type 'a t = { t_list: (Hash.t * 'a) list }

    let create _ =
      Format.printf "Random %d@." random;
      { t_list = [] }          

    let mem ht v =
      Format.printf "Random %d@." random;
      List.mem_assoc v ht.t_list          
  end
end

module Hash = struct
  type t = int
  let equal x1 x2 = x1 = x2
  let hash x = x
end

module Wrap(P: PT) = struct
  module H = Hashtbl.Make(Hash)
end

module Wrap1 = Wrap(P)
module Wrap2 = Wrap(P)
module H1 = Wrap1.H
module H2 = Wrap2.H

let () =
  let ht = H1.create 16 in
  Format.printf "%b@." (H2.mem ht 0)
Run Code Online (Sandbox Code Playgroud)

代码:ideone:https://ideone.com/5C8Muk

我在这里做的是我从Hashtbl模块创建一些函数的虚拟实现,并将它包装在Wrap我称之为'两次' 的仿函数中,创建H1并且H2可以互换使用,尽管它们是不同的模块,它们捕获不同的值random:

$ ./test.byte 
Random 501586
Random 681009
false
Run Code Online (Sandbox Code Playgroud)

这是预期的,因为正如互联网所声称的那样,OCaml仿函数是适用的.

但后来我尝试将Hash模块移到里面Wrap,程序停止编译.

module Wrap(P: PT) = struct
  module Hash = struct
    type t = int
    let equal x1 x2 = x1 = x2
    let hash x = x
  end

  module H = Hashtbl.Make(Hash)
end
Run Code Online (Sandbox Code Playgroud)

Ideone代码:https://ideone.com/Gjxc32

$ ocamlbuild test.byte
+ /home/XXX/.opam/4.04.0/bin/ocamlc.opt -c -o test.cmo test.ml
File "test.ml", line 41, characters 35-37:
Error: This expression has type 'a H1.t = 'a Hashtbl.Make(Wrap1.Hash).t
       but an expression was expected of type
         'b H2.t = 'b Hashtbl.Make(Wrap2.Hash).t
Run Code Online (Sandbox Code Playgroud)

这是为什么?我预计,如果Wrap1Wrap2是相同的模块(因为仿函数应该是合用的,对吧?),那么Wrap1.HashWrap2.Hash也相同.比较模块背后的一般规则是什么?

注意:这是另一个问题的延续如何说服ocaml两个仿函数实例是相等的.我得到的唯一答案是"OCaml仿函数是生成性的",这是错误的(至少有时候).

编辑

也许,我错误地询问模块的平等性.我真正感兴趣的是类型相等.为什么在某些情况下Wrap1.H.t等于Wrap2.H.t- 在某些情况下 - 不是.

回答

在与@Drup讨论后,对我来说事情变得更加清晰.适用性意味着以下:if A = B,then F(A) =/= F(B),but F(A).t = F(B).t.对于里面所定义的模块F(A)F(B),事情都取决于这些模块是如何定义的.在我的例子中,是否Wrap1.H.t = Wrap2.H.t取决于的定义H.在编译的变体中,Wrap1.H.t = Hashtbl(Hash).t = Wrap2.H.t.在变形无法编译,Wrap1.H.t = Hashtbl(Wrap1.Hash).tWrap2.H.t = Hashtbl(Wrap2.Hash).t和他们是不同的.

Dru*_*rup 5

"应用函子"意味着A = B暗示F(A).t = F(B).t.它并不意味着F(A).M = F(B).M.这是关于类型,而不是模块.

创建类型和创建模块之间的一个基本区别是创建类型是无副作用的(因此可以用应用行为来处理).创建模块不是副作用,因此您不能将两个不同的新模块视为相同.octachron在最后一个答案中给出了一个很好的例子.

如果要在仍具有本地模块的同时保持相等性,可以使用模块别名.如果你这样做:

module Hash0 = struct
  type t = int
  let equal x1 x2 = x1 = x2
  let hash x = x
end
module Wrap(P: PT) = struct
  module Hash = Hash0 (* the type of this module is "= Hash0" *)
  module H = Hashtbl.Make(Hash)
end
Run Code Online (Sandbox Code Playgroud)

然后该程序被接受.

请注意,即使在您的失败版本中,也Wrap1.Hash.t = Wrap2.Hash.t保持(无论其定义如何),即使模块不相等.