我无法理解 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 https://ideone.com/5C8Muk
我在这里所做的是创建一些函数的虚拟实现Hashtbl
模块并将其包装在函子中Wrap
我“调用”两次,创建H1
and 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 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
and Wrap2
是相同的模块(因为函子应该是适用的,对吧?)Wrap1.Hash
and Wrap2.Hash
也一样。比较模块背后的一般规则是什么?
注意:这是另一个问题的延续如何让 ocaml 相信两个函子实例化是相等的 https://stackoverflow.com/q/48693701/302086。我得到的唯一答案是“OCaml 函子是生成性的”,这是错误的(至少有时)。
Edit
也许,我对模块平等的询问是错误的。我真正感兴趣的是类型相等。为什么在某些情况下Wrap1.H.t
equals Wrap2.H.t
在某些情况下 - 不是。
Answer
在与@Drup 讨论之后,事情对我来说变得更加清晰了。适用性意味着:如果A = B
, then F(A) =/= F(B)
, but F(A).t = F(B).t
。对于里面定义的模块F(A)
and 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
and Wrap2.H.t = Hashtbl(Wrap2.Hash).t
,并且它们是不同的。