Когда полезно двойное принуждение?
Я наткнулся на следующее сообщение компиляции в 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
напрямую, но можно принудить его tfull
косвенно используя tphrasing
как средний шаг?
Любой указатель на теорию типов, стоящую за этим, тоже будет приветствоваться.
1 ответ
Оператор общего принуждения, также известный как двойное приведение, имеет вид
(<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
, В случае неудачи с первым оператором, следует использовать последний.
Позвольте мне попытаться выразить это более простыми словами. Принуждение возможно только в том случае, если известны как домен, так и кодомен. Тем не менее, во многих случаях вы можете применить эвристику, которая выведет домен из домена и текущий тип выражения. Эта эвристика работает, если тип выражения является наземным, не имеет рекурсий и некоторых других ограничений. По сути, если тип домена не имеет уникального наиболее общего типа, нам нужно перечислить все возможные обобщения и проверить каждую возможную комбинацию.