OCaml : type constraints in signatures
ocaml, signature, type-constraints, types
Solution
[Obsolete...
The closest you can get is defining the signature as:
module type SOURCE = sig
type 'a ctx = 'a constraint 'a = #CouchDB.ctx
type source
val get : source -> 'a ctx -> string
end
But of course, you could as well just write:
module type SOURCE = sig
type source
val get : source -> #CouchDB.ctx -> string
end
Edit: Note that OCaml uses structural typing for objects. That means that even if you wanted, you cannot get any more restrictive than the above. It does not even limit arguments to `get` to be instances of `CouchDB.ctx` or a derived class -- any object that has (at least) the same methods will be compatible. Even when you write
val get : source -> CouchDB.ctx -> string
you can pass any object that has the same methods. The type `CouchDB.ctx` is just an abbreviation for a specific structural object type that happens to match the objects generated by the class of the same name. It is not restricted to those. And just to be sure: that is considered a feature.
======]
Edit 2: With the extended example, I now see what you want and why. Unfortunately, that isn't possible directly in OCaml. You would need partially abstract types. Namely, you would need to be able to write
module type SOURCE = sig
type ctx < CouchDB.ctx
...
end
This is not available in OCaml. However, you can get close if you are willing to provide an explicit upcast in the signature:
module type SOURCE = sig
type ctx
val up : ctx -> CouchDB.ctx
type source = string
val get : source -> ctx -> string monad
end
Then, in `Cache`, you have to replace occurrences of `ctx#get` with `(S.up ctx)#get`, and likewise for `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)
Note that I also made `type source = string` transparent in the signature `SOURCE`. Without that, I cannot see how `ctx#get source` can possibly type-check in the `Cache` functor.
Problem
In my code, I have a database access context that provides elementary read/write operations, called `CouchDB.ctx`. Various modules in my application then extend that class with additional functionality, such as `Async.ctx`. I am implementing a `Cache` module which is wrapped around a `Source` module. The `Cache` module functions take a context argument and manipulate the database. Some calls are then forwarded to the `Source` module along with the context. I need to define a functor along the lines of this: ``` 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) ``` The problem is that I cannot express the fact that the context used by the `Source` module should be a subtype of `CouchDB.ctx`. The above code returns the error: ``` A type variable is unbound in this type declaration. In type #CouchDB.ctx as 'a the variable 'a is unbound ``` How do I express this type constraint ?