我正在努力(只是出于兴趣)这样做:
module type CAT = sig
type ('a, 'b) t
val id : ('a, 'a) t
val (@) : ('b, 'c) t -> ('a, 'b) t -> ('a, 'c) t
end
module Lst = struct
type ('a, 'b) t = 'a list constraint 'a = 'b
let id = []
let (@) = (@)
end
module L : CAT = Lst (* (error) *)
但我明白了:
Type declarations do not match:
type ('b, 'a) t = 'b list constraint 'a = 'b
is not included in
type ('a, 'b) t
为什么这不安全?可以看到具体类型的所有内容也可以看到约束,因此我认为您不能使用错误的类型创建某些内容(例如,使用@
参数调用(string, int) t
。
更新:对于那些说我的模块没有实现签名的人,因为它要求类型相同,请考虑以下(尽管包含List变量中的列表),尽管具有相同的行为:
module Lst = struct
type ('a, 'b) t =
List : 'a list -> ('a, 'a) t
let id = List []
let (@) (type a) (type b) (type c) (a:(b, c) t) (b:(a, b) t) : (a, c) t =
match a, b with
| List a, List b -> List (a @ b)
end
答案 0 :(得分:7)
该示例可以简化为单独的类型定义:
module type S =
sig
type ('a, 'b) t
end
module M =
struct
type ('a, 'b) t = 'a list constraint 'a = 'b
end
正如Jeffrey已经指出的那样,M
不属于S
类型,因为它允许t
的应用程序更少:根据签名S
,类型{{1}完全合法(它是格式良好的),但(int, string) t
不允许这种类型(M
不是合法类型,因为它违反了显式约束)。
所有这些都完全独立于类型是否实际上是有人居住的问题,即是否可以构造该类型的值。在您的第二个示例中,模块使相应的类型格式良好,尽管它是无人居住的。无人居住的类型是合法的,有时甚至是有用的(参见例如幻像类型的概念)。
答案 1 :(得分:5)
类型签名CAT
比Lst
模块的类型更通用。您还需要将类型约束放在抽象类型上,即type ('a, 'b) t constraint 'a = 'b
。
这给了我们以下内容:
module type CAT = sig
type ('a, 'b) t constraint 'a = 'b
val id : ('a, 'a) t
val (@) : ('b, 'c) t -> ('a, 'b) t -> ('a, 'c) t
end
由toplevel打印如下,在(@)
的签名中显示单个类型变量:
module type CAT =
sig
type ('b, 'a) t constraint 'a = 'b
val id : ('a, 'a) t
val ( @ ) : ('c, 'c) t -> ('c, 'c) t -> ('c, 'c) t
end
“类型x未包含在类型y中”形式的错误消息将类型或模块类型称为可能值集的规范,因此使用术语“包含”。
对于模块实现(Lst
),我们有一个模块类型。只有当签名与模块的原始签名一样专用(等于集合)或更专业(严格的子集)时,才允许将签名(模块类型CAT
)应用于模块。
可以写module X : sig val f : unit -> unit end = struct let f x = x end
但不是module X : sig val f : 'a -> 'a end = struct let f () = () end
。后者给出以下错误:
Error: Signature mismatch:
Modules do not match:
sig val f : unit -> unit end
is not included in
sig val f : 'a -> 'a end
Values do not match:
val f : unit -> unit
is not included in
val f : 'a -> 'a
这与在某些表达式上放置类型约束不同,在这种情况下,约束是要应用的掩码(与之交叉的集合)而不是子集。例如,即使let f : unit -> 'a = fun x -> x
的签名最终为f
,unit -> unit
的严格子集或子类型,也可以写unit -> 'a
。
答案 2 :(得分:3)
我的Lst
模块似乎不具备CAT
类型。 CAT
允许两种类型'a
和'b
独立。 Lst
模块要求它们相同。如果L
模块的类型为CAT
,那么它应该允许我制作(string, int) t
类型的内容,但它不会。
错误信息有点令人困惑,至少对我而言。