Каковы правила автоматического обновления функций? - PullRequest
0 голосов
/ 25 января 2019

В моем коде есть сценарий, в котором я бы хотел, чтобы F # автоматически передавал значения одного типа функции (например, IBase -> unit) другому, явно указанному типу функции (IDerived -> unit). Хотя это, как представляется, в целом поддерживается, я обнаружил случаи, когда это не удалось без видимой причины.

В приведенном ниже коде case1 до case8 кажутся мне эквивалентными: в каждом случае с правой стороны есть выражение типа IBase -> unit, которое связано с именем типа IDerived -> unit. Так почему же не разрешены дела 4, 5 и 7?

type IBase = interface end
type IDerived = inherit IBase

let foo (x: IBase) = ()
let bar = fun (x: IBase) -> ()

type T = T with static member (!!) (_: T) = foo
let baz = !!T

let case1: IDerived -> unit = foo                               // OK
let case2: IDerived -> unit = bar                               // OK
let case3: IDerived -> unit = baz                               // OK
let case4: IDerived -> unit = !!T                               // Expecting 'IDerived -> unit' but given 'IBase -> unit'
let case5: IDerived -> unit = upcast !!T                        // Type 'IBase -> unit' is not compatible with type 'IDerived -> unit'
let case6: IDerived -> unit = let z = !!T in z                  // OK
let case7: IDerived -> unit = fun (x: IBase) -> ()              // Expected x to have type 'IDerived' but here has type 'IBase'
let case8: IDerived -> unit = let z = fun (x: IBase) -> () in z // OK

РЕДАКТИРОВАТЬ: Чтобы уточнить, я в основном задаюсь вопросом, почему компилятор не рассматривает 8 случаев одинаково. Например, я ожидал бы, что привязка let z... в case8 будет излишней, но все же она имеет значение (по сравнению с case7). Почему?

РЕДАКТИРОВАТЬ: Вот код для демонстрации того, чего я пытаюсь достичь: https://dotnetfiddle.net/AlpdpO Он также содержит решение / обходной путь, поэтому я действительно спрашиваю больше о технических деталях / причинах, а не об альтернативных подходах. Я уже думал о том, чтобы поднять проблему с GitHub, но чувствую, что проблема, скорее всего, просто недоразумение с моей стороны.

1 Ответ

0 голосов
/ 04 февраля 2019

Вывод типа F # не кажется достаточно умным, чтобы справиться с некоторыми из этих случаев, но вы можете помочь, сделав свои определения немного более явными. Попробуйте это:

type IBase = interface end
type IDerived = inherit IBase

let foo (x: IBase) = ()

type T = T with static member (!!) (_: T) : #IBase -> unit = foo

let case4: IDerived -> unit = !!T
let case5: IDerived -> unit = upcast !!T
let case7: IDerived -> unit = fun (x: #IBase) -> ()

... и, конечно же, ваши оригинальные случаи прохождения также будут проходить. Проблема в том, что F # был слишком строг с типом функции (!!) и типом лямбды, поэтому мы можем помочь с помощью явной аннотации типа, которая говорит "IBase или любой из ее производные в порядке здесь ".

В любом случае, подайте ошибку для этих синтаксических случаев. Это не очень плохая вещь, если компилятор ошибается, когда дело доходит до типов, как это сделал компилятор F #. Гораздо худшие вещи могут случиться, если это будет слишком мягким. Здесь наихудший эффект в том, что вы должны выполнить для него некоторую работу.

Я должен отметить, что это на самом деле не имеет ничего общего с кастингом и не происходит кастинг (upcast игнорируется). Настоящая проблема заключается в строгости интерпретации. Попробуйте этот код, и вы увидите предупреждение, которое указывает, что.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...