擦除类型参数

时间:2012-07-02 14:20:09

标签: ocaml phantom-types

我有一个使用幻像类型的模块Socket来强制执行一些简单的访问控制:

module Socket : sig
  type 'a t

  val ro : string -> [ `Read ] t
  val rw : string -> [ `Read | `Write ] t
end

我想在Container.t记录中包装一个Socket.t,但是有一些方法也将幻像类型参数传播到 Container.t?

module Container : sig
  type 'a t =
  {     
  owner: string;
  socket: 'a Socket.t;
  }     
end

1 个答案:

答案 0 :(得分:4)

通常,您可以使用存在类型来忘记某些类型参数:而不是类型'a foo,您使用包含类型exists_foo的值的'a foo,对于某些{ {1}}你不知道。有几种方法可以在OCaml中构建存在类型(多态记录或第一类模块)。

对于幻像类型,您可以通过界面提供一种方法,将任何套接字转换为忘记其先前功能的“无类型套接字”。

'a

当然,您必须选择无类型套接字的可能性,您不再知道它是如何打开的。也许这不是你的想法?

您还可以将套接字保持为和类型,以保留其功能的知识:

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"))
}

最后,还有另一种方法可以实现第一个解决方案:您可以在Socket类型中允许对值进行子类型化,而不是使用top 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.t),协变量('a t)还是逆变量(+'a t) ?

如果您的心理模型是具有更多案例的幻像变体类型“更有能力”,则子类型应该从具有某些案例的变体变为具有较少案例的变体,这是较小的类型:具有{{1 }} -'a t应该a tb ta有更多情况):b应该是逆变的。

a

注意显式转换t到较小的套接字类型。