我无法理解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)
这是为什么?我预计,如果Wrap1和Wrap2是相同的模块(因为仿函数应该是合用的,对吧?),那么Wrap1.Hash和Wrap2.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).t并Wrap2.H.t = Hashtbl(Wrap2.Hash).t和他们是不同的.
"应用函子"意味着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保持(无论其定义如何),即使模块不相等.