【问题标题】:OCaml : type constraints in signaturesOCaml:签名中的类型约束
【发布时间】:2012-05-14 17:42:23
【问题描述】:

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

如何表达这种类型约束?

【问题讨论】:

  • 我很好奇你的SOURCE签名。我心中的宣言应该是module type SOURCE = sig class ctx : object inherit CouchDB.ctx end (* ... *) end;这不是你需要的吗?

标签: types ocaml signature type-constraints


【解决方案1】:

[已过时...

你能得到的最接近的是将签名定义为:

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 对对象使用 structural 类型。这意味着即使您想要,您也无法获得比上述更多的限制。它甚至不将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)#getctx#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 如何在 Cache 函子中进行类型检查。

【讨论】:

  • 我不明白如何使第一个示例适合我的情况(我的上下文类的参数为零)。第二个版本不正确,因为它期望Source.get 接受CouchDB.ctxany 子类,而实际上它只接受one (Async.ctx)。
  • 好吧,你为什么想要这样限制Source.get?据我所知,这不是必需的,也没有购买任何东西(所有子类型在 OCaml 中都是纯结构的,即使您使用类名也是如此)。我建议不要尝试将某种形式的名义子类型强加到 OCaml 上,因为这不是该语言的工作方式。 OCaml 支持结构类型而不是名义类型,多态性优于子类型。仅在类型检查需要时才约束类型。
  • 该类型被限制用于插图目的。在我的实际代码库中,该约束是从函数体中推断出来的,这显然比这里介绍的要复杂。我试图在不从我的代码中复制粘贴实际数千行的情况下提出我的问题。由于出于说明目的使用类型约束令人困惑,因此我编辑了上面的代码以进行进一步说明。
  • 啊,好的,现在我明白你在做什么了。请查看我的答案的更新。
  • 我明白了。这是我用作临时解决方案的方法,但看起来它将成为永久性解决方案。
【解决方案2】:

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

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

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

【讨论】:

  • 我已经更新了我的问题以包含一个使用仿函数的示例,以便您也可以编译它。您的解决方案阻止 SomeSource 匹配 SOURCE 签名,因为它们不允许将 ctx = Async.ctx 作为类或类类型。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-21
  • 2020-01-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多