Подтвердить что ты не робот

Когда использовать дискриминационный союз против типа записи в F #

Я пытаюсь получить основы F # clear, прежде чем переходить к сложным примерам. Материал, который я изучаю, ввел как дискриминационные союзы, так и типы записей. Я просмотрел материал для обоих, но мне все еще непонятно, почему мы будем использовать один за другим.

Большинство примеров игрушек, которые я создал, кажутся реализованными в обоих. Записи, похоже, очень близки к тому, что я считаю объектом в С#, но я стараюсь не полагаться на сопоставление с С# как способ понять F #

Итак...

  • Есть ли явная причина использовать один над другим?

  • Существуют ли определенные канонические случаи, когда они применяются?

  • Существуют ли определенные функции, доступные в одном, но не другой?

4b9b3361

Ответ 1

Подумайте об этом как о записи "и", в то время как дискриминационный союз "или". Это строка и int:

type MyRecord = { myString: string
                  myInt: int }

в то время как это значение является либо строкой, либо int, но не тем и другим:

type MyUnion = | Int of int
               | Str of string

Эта фиктивная игра может быть на экране Title, In-game или отображать окончательный результат, но только один из этих вариантов.

type Game =
  | Title
  | Ingame of Player * Score * Turn
  | Endgame of Score

Ответ 2

Используйте записи (называемые типами продуктов в теории функционального программирования) для сложных данных, которые описываются несколькими свойствами, такими как запись базы данных или какой-либо модельный объект:

type User = { Username : string; IsActive : bool }

type Body = { 
    Position : Vector2<double<m>>
    Mass : double<kg>
    Velocity : Vector2<double<m/s>> 
}

Использовать дискриминированные объединения (называемые типами сумм) для данных, которые могут быть перечислены для значений, которые могут быть перечислены. Например:

type NatNumber =
| One
| Two
| Three
...

type UserStatus =
| Inactive
| Active
| Disabled

type OperationResult<'T> =
| Success of 'T
| Failure of string

Обратите внимание, что возможные значения для дискриминированного значения объединения также являются взаимоисключающими - результат для операции может быть либо Success, либо Failure, но не одновременно.

Вы можете использовать тип записи для кодирования результата операции, например:

type OperationResult<'T> = { 
    HasSucceeded : bool
    ResultValue : 'T
    ErrorMessage : string
}

Но в случае сбоя в работе, он ResultValue не имеет смысла. Таким образом, сопоставление образцов в различной версии этого типа будет выглядеть так:

match result with
| Success resultValue -> ...
| Failure errorMessage -> ...

И если шаблон соответствует типу типа записи нашего типа операции, это будет иметь меньшее значение:

match result with
| { HasSucceeded = true; ResultValue = resultValue; ErrorMessage = _ } -> ...
| { HasSucceeded = false; ErrorMessage = errorMessage; ResultValue = _ } -> ...

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

Ответ 3

Если вы пришли из С#, вы можете понимать записи как закрытые классы с добавленными значениями:

  • Непрерывно по умолчанию
  • Структурное равенство по умолчанию
  • Простое совпадение с рисунком
  • и др.

Дискриминационные союзы кодируют альтернативы, например.

type Expr =
    | Num of int
    | Var of int 
    | Add of Expr * Expr 
    | Sub of Expr * Expr

DU выше читается следующим образом: выражение представляет собой либо целое число, либо переменную, либо добавление двух выражений или вычитание между двумя выражениями. Эти случаи не могут произойти одновременно.

Для построения записи нужны все поля. Вы также можете использовать DU внутри записей и наоборот

type Name =
    { FirstName : string;
      MiddleName : string option;
      LastName : string }

В приведенном выше примере показано, что среднее имя необязательно.

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

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

Ответ 4

Один (немного ошибочный) способ понять DU - это смотреть на него как на фантастический С# "union", в то время как запись больше похожа на обычный объект (с несколькими независимыми полями).

Еще один способ взглянуть на DU - посмотреть на DU как на двухуровневую иерархию классов, где верхний тип DU является абстрактным базовым классом, а случаи DU являются подклассами. Это представление фактически близко к реальной реализации .NET, хотя эта деталь скрыта компилятором.