Types OCaml:签名中的类型约束

Types OCaml:签名中的类型约束,types,ocaml,signature,type-constraints,Types,Ocaml,Signature,Type Constraints,在我的代码中,我有一个数据库访问上下文,它提供基本的读/写操作,称为CouchDB.ctx。然后,我的应用程序中的各种模块使用附加功能扩展该类,例如Async.ctx 我正在实现一个缓存模块,它围绕着源模块。Cache模块函数接受上下文参数并操作数据库。然后将一些调用与上下文一起转发到源模块 我需要定义一个函子,如下所示: module CouchDB = struct class ctx = object method get : string -> string optio

在我的代码中,我有一个数据库访问上下文,它提供基本的读/写操作,称为
CouchDB.ctx
。然后,我的应用程序中的各种模块使用附加功能扩展该类,例如
Async.ctx

我正在实现一个
缓存
模块,它围绕着
模块。
Cache
模块函数接受上下文参数并操作数据库。然后将一些调用与上下文一起转发到
模块

我需要定义一个函子,如下所示:

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)
如何表示此类型约束?

[过时

最接近的方法是将签名定义为:

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
然后,在
缓存中
,您必须将出现的
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)

注意,我还在签名
source
中使
type source=string
透明。如果没有这一点,我看不出
ctx#get source
怎么可能在
缓存中键入check
functor。

除非我误解了您的意图,否则这应该会起到作用:

module type SOURCE = sig
  class ctx : CouchDB.ctx
  type source
  val get : source -> ctx -> string
end
类ctx:CouchDB.ctx
是一个。OCaml文档将它们描述为

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

还有这个

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

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

我不知道如何使第一个examle适合我的情况(我的上下文类没有参数)。第二个版本是不正确的,因为它期望
Source.get
接受
CouchDB.ctx
的任何子类,而实际上它只接受一个(
Async.ctx
)。那么,为什么要限制
Source.get
?就我所见,这不是必需的,也没有任何好处(在OCaml中,所有的子类型都是纯结构化的,即使使用类名)。我建议不要在OCaml上强制使用某种形式的名词性子类型,因为这不是语言的工作方式。OCaml支持结构型而非名词型,支持多态性而非亚型。仅当类型检查需要时才约束类型。约束该类型是为了进行说明。在我的实际代码库中,该约束是从函数体推断出来的,这显然比这里介绍的更复杂。我试图在不复制粘贴代码中实际的数千行的情况下提出我的问题。由于使用类型约束来进行说明是令人困惑的,我已经编辑了上面的代码以作进一步澄清。啊,好的,现在我明白你在做什么了。请看我答案的更新。我明白了。这是我用作临时解决方案的,但看起来它将成为永久解决方案。我已更新了我的问题,以包括一个使用函子的示例,以便您也可以编译它。您的解决方案阻止了
SomeSource
SOURCE
签名匹配,因为它们不允许
ctx=Async.ctx
作为类或类类型。我对您的
SOURCE
签名很好奇。我心目中的声明应该是
模块类型SOURCE=sig class ctx:object inherit CouchDB.ctx end(*…*)end
;这不符合你的需要吗?
module type SOURCE = sig
  class ctx : CouchDB.ctx
  type source
  val get : source -> ctx -> string
end
module type SOURCE = sig
  class type ctx = CouchDB.ctx
  type source
  val get : source -> ctx -> string
end