6

在我的代码中,我有一个提供基本读/写操作的数据库访问上下文,称为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

如何表达这种类型约束?

4

2 回答 2

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函子中进行类型检查。

于 2012-05-14T18:04:56.540 回答
1

除非我误解了你所追求的,否则这应该可以解决问题:

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

class ctx : CouchDB.ctx是一个类规范。OCaml 文档将它们描述为

这是类定义签名中的对应物。如果类规范具有相同的类型参数并且它们的类型匹配,则类规范与类定义匹配。

还有这个

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

这是微妙的不同。前者需要模块中的真实类定义,后者接受类定义或类类型定义(即类类型别名)。

于 2012-05-14T18:45:48.360 回答