为什么我在实现模块类型时不能添加类型约束?

时间:2014-12-13 20:18:25

标签: ocaml type-constraints

我正在努力(只是出于兴趣)这样做:

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

3 个答案:

答案 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)

类型签名CATLst模块的类型更通用。您还需要将类型约束放在抽象类型上,即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的签名最终为funit -> unit的严格子集或子类型,也可以写unit -> 'a

答案 2 :(得分:3)

我的Lst模块似乎不具备CAT类型。 CAT允许两种类型'a'b独立。 Lst模块要求它们相同。如果L模块的类型为CAT,那么它应该允许我制作(string, int) t类型的内容,但它不会。

错误信息有点令人困惑,至少对我而言。