我无法理解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)
ideone上的代码:https://ideone.com/5C8Muk
我在这里做的是从Hashtbl
模块创建一些函数的虚拟实现,并将其包装在我调用的函数Wrap
中。两次,创建H1
和H2
可以互换使用,尽管它们是不同的模块,可以捕获random
的不同值:
$ ./test.byte
Random 501586
Random 681009
false
这是预期的,因为正如互联网所声称的那样,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
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
为什么?我希望如果Wrap1
和Wrap2
是相同的模块(因为仿函数应该是适用的,对吗?),那么Wrap1.Hash
和Wrap2.Hash
也是相同的。比较模块背后的一般规则是什么?
注意:这是另一个问题How to convince ocaml that two functor instantiations are equal的延续。我得到的唯一答案是" OCaml仿函数是生成性的"这是假的(至少有时候)。
修改
也许,我错误地询问模块的平等性。我真正感兴趣的是类型相等。为什么在某些情况下Wrap1.H.t
等于Wrap2.H.t
,在某些情况下 - 不等。
答案
与@Drup讨论后,对我来说事情变得更加清晰。 Applicativeness表示以下内容:if A = B
,F(A) =/= F(B)
,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
和<span id="submit-btn" class="btn">Submit</span>
中,它们是不同的。
答案 0 :(得分: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
然后程序被接受。
请注意,即使在您的失败版本中,Wrap1.Hash.t = Wrap2.Hash.t
仍然存在(无论其定义如何),即使模块不相等。