我在OCaml中偶然发现了以下编译消息:
This simple coercion was not fully general. Consider using a double coercion.
它发生在一个相当复杂的源代码中,但这里有一个 MNWE:
open Eliom_content.Html.D
let f_link s =
let arg : Html_types.phrasing_without_interactive elt list = [pcdata "test"] in
[ Raw.a ~a:[a_href (uri_of_string (fun () -> "test.com"))] arg ]
type tfull = (string -> Html_types.flow5 elt list)
type tphrasing = (string -> Html_types.phrasing elt list)
let a : tfull = ((f_link :> tphrasing) :> tfull)
let b : tfull = (f_link :> tfull)
您可以使用ocamlfind ocamlc -c -package eliom.server -thread test.ml
编译此示例,并安装 Eliom 6。
错误发生在最后一行,OCaml 编译器抱怨f_link
无法转换为类型tfull
。
有人可以向我解释为什么不可能直接胁迫f_link
tfull
,但可以用tphrasing
作为中间步骤间接胁迫它tfull
?
任何指向其背后的类型论的指针也将受到欢迎。
一般强制算子,又称双重胁迫,有形式
(<exp> : <subtype> :> <type>)
有时可以省略<subtype>
类型,在这种情况下,它称为单一强制。所以在你的例子中,正确的胁迫应该是这样的:
let a : tfull = (f_link : f_link_type :> tfull)
其中f_link_type
是f_link
函数的一种类型。
手册中描述了它可能失败的原因:
前一个运算符有时无法强制表达式
expr
从类型typ1
到类型typ2
即使类型typ1
是类型的子类型typ2
:在当前的实现中,它只扩展了两个级别的类型 包含对象和/或多态变体的缩写,保留 仅当递归在类类型中显式时(对于对象)。如 上述算法的一个例外,如果推断的类型都expr
typ
是地面的(即不包含类型变量),前者 运算符的行为与后者一样,采用推断的expr
类型 作为typ1
.如果前一个操作员发生故障,则后者 应该使用。
让我试着用更简单的术语来表达它。只有在域和共域都已知的情况下,强制才有可能。但是,在许多情况下,您可以应用启发式方法,该启发式方法将从协域和当前表达式类型推断域。如果表达式类型是 ground、没有递归和其他一些限制,则此启发式方法有效。基本上,如果域类型没有唯一的最通用类型,我们需要枚举所有可能的泛化并检查每个可能的组合。