2012-07-02 139 views
1

我有一个模块插槽使用虚拟类型执行一些简单的访问控制:擦除类型参数

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 
+2

定义“套接字”字段的类型为'[''读取| 'Write] Socket.t'? –

回答

4

在一般情况下,你可以使用存在的类型忘掉某些类型的参数:替代型'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 tb t应该有aba有更多的情况下):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更小的套接字类型。

+0

有趣的是,你可以声明多态记录和一流模块作为两种可能的技术,但在你的答案中都不使用。 –

+0

多态性记录和一流模块可用于构建存在型 - 使用更新版本的OCaml,GADT也可用,并且比这些更方便。存在被用来非正式地构建一个类型为“exists_foo”的类型,它比所有的“a foo”都要大。如果你知道域名是所有可能的''a''类型的域,那么你不需要它们,并且这个域具有最大的元素'top';在这种情况下,只要写完从''a foo'到'top foo'的显式转换,你就完成了。 – gasche