Проверка типа выдает ошибку для выражения union test-expression

Мой вопрос касается следующей программы.

open System

// Types
type Car (brand: string) =
    member _.Brand = brand
type BMW () =
    inherit Car "BMW"
type Pet =
    | Cat
    | Dog

[<EntryPoint>]
let main argv =
    // Match subtype of test-expression type: ok
    let car = Car "Mercedes"
    let carResult =
        match car with
        | :? BMW -> 1
        | _ -> 0
    // Match type of test-expression: "will always hold" warning
    let bmw = BMW ()
    let bmwResult =
        match bmw with
        | :? BMW -> 1
        | _ -> 0
    // Catch any exception: "will always hold" warning
    let exceptionResult =
        try
            1/0
        with
            | :? Exception -> 2
    // Match type of test-expression (union type): "The type 'Pet' does not have any proper subtypes" error
    let cat = Cat // this has type Pet
    let catResult =
        match cat with
        | :? Pet -> 1
        | _ -> 0
    0

В первом тесте тестовое выражение имеет тип Car, подтипом которого является тип в тестовом шаблоне типа BMW, и нет никаких предупреждений или ошибок. Во втором и третьем тестах тип шаблона проверки типа совпадает с типом тестового выражения, и понятно, что выдается предупреждение, потому что, когда программист проверяет, действительно ли BMW является BMW или Exception на самом деле Exception, скорее всего, это логическая ошибка.

Последний тест имеет ту же форму, что и тесты два и три: выражение-тест имеет тип Pet, а шаблон теста типа также имеет тип Pet. Так почему же в этом случае выдает ошибку? Ошибка говорит The type 'Pet' does not have any proper subtypes.... Но BMW не имеет подтипов и не выдает этой ошибки. Кроме того, страница Pattern Matching (в разделе «Шаблон проверки типа») говорит, что «Если тип ввода соответствует (или производному типу) типу, указанному в шаблоне, совпадение завершается успешно». Pet соответствует Pet, следовательно, и т. д. Почему тип объединения обрабатывается по-разному?


person CarbonFlambe    schedule 11.03.2020    source источник


Ответы (1)


Возможно, сообщение об ошибке сформулировано слишком расплывчато. Дело не в том, что тип Pet не имеет подтипов, а в том, что он не может иметь подтипы.

Поскольку BMW — это класс, он может иметь подтип из другой сборки, которая была скомпилирована после той, в которой определен сам BMW.

Но для Pet этого произойти не может, потому что у типов-сумм не может быть подтипов, и поэтому сопоставление наследования для этих типов запрещено.

Также обратите внимание, что источником ошибки является тип сопоставляемой переменной, а не тип шаблона. Так, например, это скомпилируется без ошибок:

let n : obj = null

match n with
| :? Pet -> "is a pet"
| _ -> "no idea"

Это работает, потому что n относится к типу obj, у которого действительно есть подтипы, одним из которых является Pet (в конце концов, это .NET, все является объектом). Но сопоставление вашей переменной cat не работает, потому что эта переменная по своей сути имеет тип без подтипа.

person Fyodor Soikin    schedule 11.03.2020