2012-07-02 6 views
1

У меня есть модуль гнездо с использованием типов фантомных для обеспечения некоторого простого контроля доступа:Стирания параметр типа

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

Что не так с определением поля 'socket' для типа' ['Read | «Написать] 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 следует иметь 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 к меньшему типу сокета.

+0

Интересно, что вы указываете полиморфные записи и первоклассные модули как два возможных метода, но не используете ни один из них в своем ответе. –

+0

Полиморфные записи и первоклассные модули могут использоваться для построения экзистенциального типа - с более новыми версиями OCaml, GADT также доступны и более удобны, чем таковые. Экзистенции используются, в неофициальном порядке, для создания типа 'exist_foo', который« больше », чем все« foo ». Они вам не нужны, если вы знаете домен в домене всех возможных типов '' a' в вашем конкретном случае, и этот домен имеет самый большой элемент 'top'; в этом случае просто напишите явное преобразование из '' foo' в 'top foo', вы закончили. – gasche

Смежные вопросы