Непоследовательное поведение при сопоставлении с образцом - PullRequest
4 голосов
/ 03 декабря 2011
type Bar = A | B
type Foo = C of Bar | D of Bar
let case = Unchecked.defaultof<Foo>;;

match case with
| C A -> ""
| C B -> ""
| _ -> "Matches";;

match case with
| C A -> ""
| D B -> ""
| _ -> "Throws"

Быстро просматривая спецификацию языка F #, ничего о нулевом тесте (который я не могу сделать в любом случае) казалось связанным, и оба типа кажутся ссылочным типом (AFAIK).

Я быпредположим, что в первом случае поведение будет правильным.

Ответы [ 5 ]

3 голосов
/ 03 декабря 2011

Итак, читая спецификацию, первая подсказка здесь

b) Типы с нулевым значением в качестве ненормального значения.Это типы, которые не допускают нулевого литерала, но имеют нулевое значение как ненормальное значение.

Типы в этой категории:

o Весь список F #, запись, кортеж, функция, класси типы интерфейсов.

o Все типы объединения F #, кроме тех, для которых в качестве нормального значения указано значение null (как описано в следующем абзаце).

Для этих типов использование литерала null имеет видпрямо не разрешеноОднако, строго говоря, можно сгенерировать нулевое значение для этих типов, используя определенные функции, такие как Unchecked.defaultof.Для этих типов ноль считается ненормальным значением.Поведение операций по отношению к нулевым значениям определено в §6.9.

Это, по-видимому, предполагает, что при передаче нулевого значения для вашего объединения может быть какое-то неопределенное поведение, так как значение«ненормальный», раздел 6.9 не особенно полезен

Глядя на определение _, кажется, что вы правы, что это ошибка - она ​​гласит

7.1.7 Шаблоны подстановочных знаков

Шаблон _ является шаблоном подстановочных знаков и соответствует любому входу.Например:

позвольте категоризировать x =

match x with

| 1 -> 0

| 0 -> 1

| _ -> 0

Я думаю, что самые важные подсказки, хотя позже, где перечислены скомпилированные методы для DU, перечислены

8.5.3 Скомпилированная форма типов объединения для использования на других языках CLI

Скомпилированный тип объединения U будет иметь:

· Одно свойство статического геттера CLI UC для каждого случая нулевого объединения C.Это приведет к получению одноэлементного объекта, представляющего этот случай.

· Один вложенный тип CLI UC для каждого случая ненулевого объединения C. Этот тип будет иметь свойства экземпляра Item1, Item2 .... для каждого поля объединенияcase или свойство экземпляра Item, если имеется только одно поле.Скомпилированный тип объединения только с одним случаем не имеет вложенного типа.Вместо этого сам тип объединения играет роль типа case.

· Один статический метод CLI U.NewC для каждого ненулевого случая объединения C. Это создаст объект для этого случая.

· Одно свойство экземпляра CLI u.IsC для каждого случая C, которое возвращает true или false для случая.

· Одно свойство экземпляра CLI u.Tag для каждого случая C, которое выбирает или вычисляет целочисленный тег, соответствующийcase.

Из этого вы можете видеть, что все методы для проверки являются методами экземпляра, которые требуют ненулевого значения.Синус null является "ненормальным", сгенерированный код не беспокоит проверки, поэтому он выдает.

Я думаю, вы могли бы утверждать, что это на самом деле ошибка, основанная на определении _.Однако для его исправления потребуется вставлять нулевые проверки перед каждой проверкой соответствия шаблону DU, что значительно замедлит код, поэтому я сомневаюсь, будет ли это исправлено

3 голосов
/ 03 декабря 2011

Я думаю, что все ставки отменены, как только вы используете Unchecked.defaultof<_> (таким образом, "Unchecked" ;-)). Нулевое значение не считается допустимым значением для типа Foo с точки зрения F # (хотя оно с точки зрения .NET), поэтому я не думаю, что семантика сопоставления с образцом определена.

Что вы пытаетесь сделать?

2 голосов
/ 03 декабря 2011

Сравнение с DU в двух случаях компилируется в if/else с типовыми тестами.Если вы переводите свои примеры, поведение становится очевидным.

match case with
| C A -> ""
| C B -> ""
| _ -> "Matches"

переводится в

if (case is C) 
  ...
else
  "Matches"

, а

match case with
| C A -> ""
| D B -> ""
| _ -> "Throws"

переводится в

if (case is D)
  ...
else //must be C
  //check tag  BANG! NRE

И пример kvb: match case with | C _ -> "C" | D _ -> "D"

if (case is D)
  //...
else //must be C
  "C"

Я полагаю, вы могли бы рассматривать это как разумную оптимизацию (против if (case is D) {...} else if (case is C) {...} else { MatchFailureException }), учитывая, что поведение null не определено.

Добавьте третий случай к Foo, и проблема исчезнет.

2 голосов
/ 03 декабря 2011

Описание метода Unchecked.defaultof заканчивается предложением «Эта функция небезопасна в том смысле, что некоторые значения F # не имеют правильных нулевых значений», что именно здесь и происходит.

Попробуйтеthis:

let isNull = (case = null) ;;

  let isNull = (case = null) ;;
  ---------------------^^^^

stdin(3,22): error FS0043: The type 'Foo' does not have 'null' as a proper value
> 

DU предназначены для того, чтобы быть неизменными и не иметь правильных нулевых значений.

0 голосов
/ 03 декабря 2011

В настоящее время я не могу подтвердить это, но не приведет ли атрибут AddNullLiteral к типам к совпадению?

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