OCaml:签名中的类型约束

Vic*_*let 5 ocaml types 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)
Run Code Online (Sandbox Code Playgroud)

问题是我无法表达Source模块使用的上下文应该是子类型的事实CouchDB.ctx.上面的代码返回错误:

A type variable is unbound in this type declaration.
In type #CouchDB.ctx as 'a the variable 'a is unbound
Run Code Online (Sandbox Code Playgroud)

如何表达此类型约束?

And*_*erg 5

[过时...

您可以获得的最接近的是将签名定义为:

module type SOURCE = sig
  type 'a ctx = 'a constraint 'a = #CouchDB.ctx
  type source
  val get : source -> 'a ctx -> string 
end
Run Code Online (Sandbox Code Playgroud)

但当然,你也可以写:

module type SOURCE = sig
  type source
  val get : source -> #CouchDB.ctx -> string 
end
Run Code Online (Sandbox Code Playgroud)

编辑:请注意,OCaml使用对象的结构类型.这意味着即使你想要,你也不能比上面的限制更多.它甚至不限制get作为实例CouchDB.ctx或派生类的参数 - 任何具有(至少)相同方法的对象都是兼容的.即使你写作

  val get : source -> CouchDB.ctx -> string 
Run Code Online (Sandbox Code Playgroud)

您可以传递任何具有相同方法的对象.该类型CouchDB.ctx只是特定结构对象类型的缩写,恰好匹配由同名类生成的对象.它不仅限于此.而且可以肯定:这被视为一项功能.

======]

编辑2:通过扩展示例,我现在可以看到您想要的和原因.不幸的是,这在OCaml中是不可能的.你需要部分抽象的类型.也就是说,你需要能够写作

module type SOURCE = sig
  type ctx < CouchDB.ctx
  ...
end
Run Code Online (Sandbox Code Playgroud)

这在OCaml中不可用.但是,如果您愿意在签名中提供明确的upcast,则可以关闭:

module type SOURCE = sig
  type ctx
  val up : ctx -> CouchDB.ctx
  type source = string
  val get : source -> ctx -> string monad
end
Run Code Online (Sandbox Code Playgroud)

然后,在Cache,你必须替换ctx#getwith的出现(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)
Run Code Online (Sandbox Code Playgroud)

请注意,我type source = string在签名中也是透明的SOURCE.没有它,我无法看到如何ctx#get sourceCache仿函数中进行类型检查.