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を使用します。知らないものもあります。OCaml で存在型を構築する方法はいくつかあります (ポリモーフィック レコードまたはファーストクラス モジュール)。exists_foo'a foo'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"))
}

もちろん、ソケットがどのように開かれたかがわからない、型指定されていないソケットに対して可能なことを選択する必要があります。多分それはあなたが考えていたものではありませんか?

ソケットを合計型に保持して、その機能の知識を保持することもできます。

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) ですか?

あなたのメンタルモデルが、より多くのケースを持つファントムバリアントタイプが「より有能」である場合、サブタイプは、いくつかのケースを持つバリアントから、より小さなタイプであるケースの少ないバリアントに移行する必要があります: to have a tb tone should have 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 に答える