在一般情况下,你可以使用存在的类型忘掉某些类型的参数:替代型'a foo
,您使用exists_foo
包含'a foo
类型的值,对于一些'a
你不知道的。有几种方法可以构建OCaml中的存在类型(多态记录或第一类模块)。
在幻影类型的情况下,可以提供一种方式,通过你的接口,任何插座转换为“无类型插座”已经忘记了以前的能力。
type untyped
module Socket : sig
type 'a t
val ro : string -> [ `Read ] t
val rw : string -> [ `Read | `Write ] t
val untyped : 'a t -> untyped t
end = struct
type 'a t = string
let ro s = s
let rw s = s
let untyped s = s
end
type t = { owner : string; socket : untyped Socket.t }
let test = {
owner = "foo";
socket = Socket.(untyped (ro "bar"))
}
当然,你要选择什么是可能的非类型化插座,为此,你不知道它是怎么了打开。也许这不是你想到的?
您也可以保持插座之类,以保留其能力的知识:
module Socket : sig
type 'a t
val ro : string -> [ `Read ] t
val rw : string -> [ `Read | `Write ] t
val read : [> `Read ] t -> unit
val write : [> `Write ] t -> unit
end = struct
type 'a t = string
let ro s = s
let rw s = s
let read _ =()
let write _ =()
end
type some_socket =
| Ro of [ `Read ] Socket.t
| Rw of [ `Read | `Write ] Socket.t
type t = { owner : string; socket : some_socket }
let test = {
owner = "foo";
socket = Ro (Socket.ro "bar")
}
let write container = match container.socket with
| Ro _ -> failwith "write not allowed"
| Rw s -> Socket.write s
最后,是实现第一个解决方案的另一种方式:而是采用了顶级untyped
类型,您可以允许在类型为Socket的子类型上进行分类。为此,您需要指定,在你的界面,Socket.t
类型的变化:这是不变的('a t
),协(+'a t
)或逆变(-'a t
)?
如果你的心智模型是一个具有更多病例的幻像变体类型是“更有能力”,那么子类型应该从具有一些病例的变体到具有较少病例的变体,这是更小的类型:具有a t
≤ b t
应该有a
≥b
(a
有更多的情况下):t
应该是逆变。
module Socket : sig
type -'a t
val ro : string -> [ `Read ] t
val rw : string -> [ `Read | `Write ] t
val read : [> `Read ] t -> unit
val write : [> `Write ] t -> unit
end = struct
type 'a t = string
let ro s = s
let rw s = s
let read _ =()
let write _ =()
end
type t = { owner : string; socket : [ `Read ] Socket.t }
let test = {
owner = "foo";
socket = (Socket.rw "bar" :> [ `Read ] Socket.t)
}
请注意显式转换(socket :> [ `Read ]) Socket.t
更小的套接字类型。
定义“套接字”字段的类型为'[''读取| 'Write] Socket.t'? –