在我的代码中,我有一个数据库访问上下文,它提供了基本的读/写操作,称为CouchDB.ctx
。我的应用程序中的各种模块然后使用其他功能扩展该类,例如Async.ctx
。
我正在实现一个Cache
模块,该模块包含在Source
模块中。 Cache
模块函数接受上下文参数并操纵数据库。然后,一些调用将与上下文一起转发到Source
模块。
我需要按照以下方式定义一个仿函数:
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)
问题在于我无法表达Source
模块使用的上下文应该是CouchDB.ctx
的子类型这一事实。上面的代码返回错误:
A type variable is unbound in this type declaration.
In type #CouchDB.ctx as 'a the variable 'a is unbound
如何表达此类型约束?
答案 0 :(得分:5)
[作废...
您最接近的是将签名定义为:
module type SOURCE = sig
type 'a ctx = 'a constraint 'a = #CouchDB.ctx
type source
val get : source -> 'a ctx -> string
end
但当然,你也可以写一下:
module type SOURCE = sig
type source
val get : source -> #CouchDB.ctx -> string
end
编辑:请注意,OCaml使用结构键入对象。这意味着即使你想要,你也不能比上面的限制更多。它甚至不会将get
的参数限制为CouchDB.ctx
或派生类的实例 - 任何具有(至少)相同方法的对象都是兼容的。即使你写作
val get : source -> CouchDB.ctx -> string
您可以传递具有相同方法的任何对象。类型CouchDB.ctx
只是特定结构对象类型的缩写,恰好匹配由同名类生成的对象。它不仅限于此。而且可以肯定:这被视为一种功能。
==]
编辑2:通过扩展示例,我现在可以看到您想要的内容和原因。不幸的是,这在OCaml中是不可能的。您需要部分抽象类型。也就是说,你需要能够写
module type SOURCE = sig
type ctx < CouchDB.ctx
...
end
这在OCaml中不可用。但是,如果您愿意在签名中提供明确的上传,则可以关闭:
module type SOURCE = sig
type ctx
val up : ctx -> CouchDB.ctx
type source = string
val get : source -> ctx -> string monad
end
然后,在Cache
中,您必须将ctx#get
的匹配项替换为(S.up ctx)#get
,同样替换为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)
请注意,我还在签名type source = string
中使SOURCE
透明。没有它,我无法看到ctx#get source
如何在Cache
仿函数中进行类型检查。
答案 1 :(得分:1)
除非我误解了你的想法,否则这应该可以解决问题:
module type SOURCE = sig
class ctx : CouchDB.ctx
type source
val get : source -> ctx -> string
end
class ctx : CouchDB.ctx
是class-specification。 OCaml文档将它们描述为
这是类定义签名的对应物。如果类规范具有相同的类型参数且类型匹配,则类规范与类定义匹配。
还有这个
module type SOURCE = sig
class type ctx = CouchDB.ctx
type source
val get : source -> ctx -> string
end
略有不同。前者需要模块中的实类定义,后者接受类定义或classtype定义(即类类型别名)。