OCaml:签名中的类型约束

时间:2012-05-14 17:42:23

标签: types ocaml signature type-constraints

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

如何表达此类型约束?

2 个答案:

答案 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.ctxclass-specification。 OCaml文档将它们描述为

  

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

还有这个

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

略有不同。前者需要模块中的实类定义,后者接受类定义或classtype定义(即类类型别名)。