1

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

module Socket : sig
  type 'a t

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

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

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

1 回答 1

4

通常,您可能会使用存在类型来忘记某些类型参数:'a foo您使用的不是 type ,而是exists_foo包含 type 的值'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"))
}

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

您还可以将套接字保持为 sum 类型,以保留对其功能的了解:

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 tb t一个应该具有ab(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转换为较小的套接字类型。

于 2012-07-02T22:08:28.380 回答