2012-05-14 33 views
5

Trong mã của tôi, tôi có một bối cảnh truy cập cơ sở dữ liệu cung cấp các hoạt động đọc/ghi sơ cấp, được gọi là CouchDB.ctx. Các mô-đun khác nhau trong ứng dụng của tôi sau đó mở rộng lớp đó với chức năng bổ sung, chẳng hạn như Async.ctx.OCaml: loại ràng buộc trong chữ ký

Tôi đang triển khai mô-đun Cache được bao quanh mô-đun Source. Các hàm Cache mô-đun lấy một đối số ngữ cảnh và thao tác cơ sở dữ liệu. Một số cuộc gọi sau đó được chuyển tiếp đến mô-đun Source cùng với ngữ cảnh.

tôi cần phải xác định một functor dọc theo dòng này:

module CouchDB = struct 
    class ctx = object 
    method get : string -> string option monad 
    method put : string -> string -> unit monad 
    end 
end 

module AsyncDB = struct 
    class ctx = object 
    inherit CouchDB.ctx 
    method delay : 'a. float -> (ctx -> 'a monad) -> 'a monad 
    end 
end 

module type SOURCE = sig 
    class ctx = #CouchDB.ctx (* <-- incorrect *) 
    type source 
    val get : source -> ctx -> string monad 
end 

module Cache = functor(S:SOURCE) -> struct 
    class ctx = S.ctx 
    type source = S.source 
    let get source ctx = 
    bind (ctx # get source) (function 
    | Some cache -> return cache 
    | None -> 
     bind (S.get source ctx) 
     (fun data -> bind (ctx # put source data) 
         (fun() -> return data)) 
end 

module SomeSource = struct 
    class ctx = AsyncDB.ctx 
    type source = string 
    let get s ctx = 
    ctx # async 300 (some_long_computation s) 
end 

module SomeCache = Cache(SomeSource) 

Vấn đề là tôi không thể diễn tả thực tế là bối cảnh được sử dụng bởi các module Source phải là một subtype của CouchDB.ctx. Mã trên trả về lỗi:

A type variable is unbound in this type declaration. 
In type #CouchDB.ctx as 'a the variable 'a is unbound 

Làm cách nào để thể hiện ràng buộc loại này?

+0

Tôi tò mò về chữ ký 'SOURCE' của bạn. Khai báo trong tâm trí của tôi phải là 'loại mô-đun SOURCE = sig lớp ctx: đối tượng kế thừa kết thúc CouchDB.ctx (* ... *) end'; điều này không làm những gì bạn cần? –

Trả lời

5

[Obsolete ...

Gần nhất bạn có thể nhận được xác định chữ ký như:

module type SOURCE = sig 
    type 'a ctx = 'a constraint 'a = #CouchDB.ctx 
    type source 
    val get : source -> 'a ctx -> string 
end 

Nhưng tất nhiên, bạn có thể cũng chỉ cần viết:

module type SOURCE = sig 
    type source 
    val get : source -> #CouchDB.ctx -> string 
end 

Sửa : Lưu ý rằng OCaml sử dụng cấu trúc nhập cho các đối tượng. Điều đó có nghĩa là ngay cả khi bạn muốn, bạn không thể nhận được bất kỳ hạn chế hơn so với ở trên. Nó thậm chí không giới hạn các đối số cho get là các cá thể của CouchDB.ctx hoặc một lớp dẫn xuất - bất kỳ đối tượng nào có (ít nhất) các phương thức tương tự sẽ tương thích. Ngay cả khi bạn viết

val get : source -> CouchDB.ctx -> string 

bạn có thể vượt qua bất kỳ đối tượng rằng có những phương pháp tương tự. Kiểu CouchDB.ctx chỉ là một từ viết tắt cho một kiểu đối tượng cấu trúc cụ thể xảy ra để khớp với các đối tượng được tạo bởi lớp có cùng tên. Nó không bị giới hạn đối với những người đó. Và chỉ để chắc chắn: đó được coi là một tính năng.

======]

Chỉnh sửa 2: Với ví dụ mở rộng, giờ tôi thấy bạn muốn gì và tại sao. Thật không may, điều đó là không thể trực tiếp trong OCaml. Bạn sẽ cần một phần trừu tượng loại. Cụ thể, bạn cần phải có thể viết

module type SOURCE = sig 
    type ctx < CouchDB.ctx 
    ... 
end 

Điều này không có sẵn trong OCaml. Tuy nhiên, bạn có thể nhận được gần nếu bạn sẵn sàng để cung cấp một sự liệng lên rõ ràng trong chữ ký:

module type SOURCE = sig 
    type ctx 
    val up : ctx -> CouchDB.ctx 
    type source = string 
    val get : source -> ctx -> string monad 
end 

Sau đó, trong Cache, bạn phải thay thế xuất hiện của ctx#get với (S.up ctx)#get, và tương tự như vậy cho ctx#put.

module Cache = functor (S:SOURCE) -> struct 
    type ctx = S.ctx 
    type source = S.source 
    let get source ctx = 
    bind ((S.up ctx)#get source) ... 
end 

module SomeSource = struct 
    type ctx = AsyncDB.ctx 
    let up ctx = (ctx : ctx :> CouchDB.ctx) 
    type source = string 
    let get s ctx = ... 
end 

module SomeCache = Cache (SomeSource) 

Lưu ý rằng tôi cũng đã làm type source = string minh bạch trong chữ ký SOURCE. Nếu không có điều đó, tôi không thể thấy cách ctx#get source có thể có thể nhập-kiểm tra trong hàm f2 Cache.

+0

Tôi không hiểu làm thế nào tôi có thể làm cho examle đầu tiên phù hợp với tình hình của tôi (lớp ngữ cảnh của tôi có thông số bằng không). Phiên bản thứ hai là không chính xác, vì nó mong đợi 'Source.get' chấp nhận * bất kỳ lớp con nào của' CouchDB.ctx', khi thực tế nó chỉ chấp nhận * one * ('Async.ctx'). –

+0

Vâng, tại sao bạn * muốn * hạn chế 'Source.get' như thế? Theo như tôi thấy, điều đó là không cần thiết, cũng không mua bất cứ thứ gì (tất cả subtyping là cấu trúc hoàn toàn trong OCaml, ngay cả khi bạn sử dụng tên lớp). Tôi khuyên bạn không nên cố gắng ép buộc một số dạng phụ danh định vào OCaml, vì đó không phải là cách ngôn ngữ hoạt động. OCaml ủng hộ việc đánh máy cấu trúc trên danh nghĩa và tính đa hình qua phân loại. Chỉ hạn chế các loại nếu cần thiết cho việc kiểm tra kiểu. –

+0

Loại đó bị hạn chế cho * mục đích minh họa *. Trong cơ sở mã thực sự của tôi, ràng buộc đó được suy ra từ phần thân của hàm, rõ ràng là phức tạp hơn so với những gì được trình bày ở đây. Tôi đang cố gắng trình bày sự cố của mình mà không cần sao chép-dán hàng nghìn dòng thực từ mã của tôi. Vì việc sử dụng các ràng buộc kiểu cho mục đích minh họa là khó hiểu, tôi đã chỉnh sửa mã ở trên để làm rõ thêm. –

1

Trừ khi tôi là sự hiểu lầm gì bạn đang sau thì điều này nên làm như lừa:

module type SOURCE = sig 
    class ctx : CouchDB.ctx 
    type source 
    val get : source -> ctx -> string 
end 

class ctx : CouchDB.ctx là một class-specification. Các tài liệu OCaml mô tả chúng như

This is the counterpart in signatures of class definitions. A class specification matches a class definition if they have the same type parameters and their types match.

Ngoài ra còn có này

module type SOURCE = sig 
    class type ctx = CouchDB.ctx 
    type source 
    val get : source -> ctx -> string 
end 

đó là tinh tế khác nhau. Cái trước yêu cầu một định nghĩa lớp thực trong mô-đun mà sau này chấp nhận một định nghĩa lớp hoặc một định nghĩa kiểu gõ (tức là một bí danh kiểu lớp).

+0

Tôi đã cập nhật câu hỏi của mình để bao gồm một ví dụ về cách sử dụng hàm functor, để bạn cũng có thể biên dịch nó. Các giải pháp của bạn đã ngăn không cho 'SomeSource' khớp với chữ ký' SOURCE', vì chúng không cho phép 'ctx = Async.ctx' hoặc là các lớp hoặc kiểu lớp. –

Các vấn đề liên quan