OCaml - 来自类型别名的混淆(警告 40)

OCaml - confusion from type alias (warning 40)

我不明白为什么 OCaml 无法弄清楚这里没有混淆的余地:下面的 anint 只能是 A 的。

module A = struct
  type test = Graphics.status
end

module type ASIG = sig
  type test = A.test
  val atest : test 
end

module Func (H : ASIG) = struct
  let _ = let open H in atest.key 
end

然而,它提高了

Warning 40: key was selected from type Graphics.status.
It is not visible in the current scope, and will not 
be selected if the type becomes unknown.

如何在不禁用警告的情况下告诉它"it's fine"?

我知道我可以通过打开 A 来解决它。但是,如果 H 定义了自己的函数和类型类似于——但不等于——A,那么它将产生不必要的冲突。我也知道我可以复制定义,但这违背了类型别名的目的,并且涉及很多不必要的代码重复。也许没有解决方案,但我想知道为什么 OCaml 在这个问题上如此愚蠢:类型别名应该也意味着构造函数和记录字段别名,不是吗?

嗯,发生这种情况是因为模块签名 ASIG 需要查看类型 test 的定义以实现 A。这通常会导致类型的可见性出现问题,有时需要重复类型定义,其中合同满足实现而不是引用它。

我们如何解决这个警告?在 ASIG 中,我们需要像在实现中那样明确地做 type test = { anint: int },而不是定义 type test = A.test,所以:

module ASIG = sig
  type test = { anint: int }
  val atest : test
end

module A = struct
  type test = { anint: int }
end

module Func (H : ASIG) = struct
  let _ = let open H in atest.anint
end

H 模块将无法在其范围内查看 anint,否则,因为签名具有链接到实现的类型(合同)。这也是 OCaml 哲学的核心概念,即隔离签名和实现并避免签名依赖于实现。

您可以在引用字段 key 时在本地简单地打开模块 定义原始类型,如下所示:

module A = struct
  type test = Graphics.status
end

module type ASIG = sig
  type test = A.test
  val atest : test 
end

module Func (H : ASIG) = struct
  let _ = let open H in atest.Graphics.key 
end

或者如果您需要引用多个字段:
let _ = let open H in Graphics.(atest.key, atest.button)