Как заставить F # выводить общий базовый тип? - PullRequest
4 голосов
/ 27 марта 2012

Примите во внимание следующее:

module Module1 =
    type A() = class end
    type B() = inherit A()
    type C() = inherit A()

    let f x = if x > 0 then new B() else new C()

В последней строке выдается ошибка о том, что тип B ожидается, но вместо него найден тип C.Хорошо, я могу притвориться, что понимаю: компилятор не знает, какую общую базу выводить, если их много.

Но угадайте, что?Даже когда я указываю тип функции, он все равно не работает:

    let f x : A = if x > 0 then new B() else new C()

Теперь это дает мне две ошибки: " Ожидается, B найдено " и " Aожидается, C найдено".WTF?Почему он не видит, что и B, и C неявно конвертируются в A?

Да, я знаю, что мог бы использовать upcast, вот так:

    let f x : A = if x > 0 then upcast new B() else upcast new C()

Но угадайтечто опять)?upcast работает только при явном объявлении типа функции!Другими словами, это:

    let f x = if x > 0 then upcast new B() else upcast new C()

по-прежнему выдает ошибку.

WTF ?!Мне действительно нужно добавить 50% шума в мою программу, чтобы выручить компилятор?Что со всей этой шумихой по поводу того, что код F # чистый и бесшумный?

Почему-то кажется, что это не может быть правдой.Итак, вопрос: я что-то упустил?Как мне сделать это одновременно компактным и рабочим?

Ответы [ 2 ]

10 голосов
/ 27 марта 2012

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

if b then 
    e1 
else 
    e2

неявно обрабатывалось больше как

if b then (e1 :> 'a) else (e2 :> 'a)

с компилятором, дополнительно выводящим 'a как наименее верхнийограниченный в иерархии типов, основанный на типах, которые в противном случае были бы выведены для e1 и e2.

Технически возможно сделать это, и я не могу окончательно сказать, почему F # не делаеттак не работает, но здесь есть предположение: если бы операторы if вели себя таким образом, то никогда не было бы ошибкой иметь разные типы в ветвях if и else, поскольку их всегда можно объединить, неявно передавая их.до obj.Однако на практике это почти всегда ошибка программиста - вы почти всегда хотите, чтобы типы были одинаковыми (например, если я возвращаю символ из одной ветви и строку из другой, я, вероятно, хотел бы вернуть строки из обеих, а не obj).При неявном обновлении вы просто усложняете обнаружение этих ошибок.

Кроме того, в F # относительно редко приходится иметь дело со сложными иерархиями наследования, за исключением, возможно, взаимодействия с другим кодом .NET.В результате это очень незначительное ограничение на практике.Если вы ищете синтаксически более короткое решение, чем upcast, вы можете попробовать :> _, который будет работать до тех пор, пока есть что-то, что ограничивает тип (либо аннотация на общий результат, либо конкретное приведение на него).филиалов).

2 голосов
/ 27 марта 2012

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

let f x = if x > 0 then (new B() :> A) else (new C() :> A)

Здесь вы можете найти дополнительную информацию: F # потребность в приведении

А вот еще одна замечательная дискуссия по этому вопросу.

...