Варианты или полиморфные варианты?

Я заметил, что среди программистов OCaml, которых я знаю, некоторые из них всегда используют полиморфные варианты (варианты, которые не объявлены, с префиксом backquote), в то время как другие никогда не используют полиморфные варианты и предпочитают варианты, объявленные в типах.

За исключением причин производительности (полиморфные варианты в настоящее время скомпилированы менее эффективно, чем простые варианты), как разработчики OCaml выбирают между ними?

Ответы

Ответ 1

Мое использование можно разделить на следующие 5 категорий. 1. интерфейс 2. модульность 3. четкость 4. краткость 5. трюки

  • Если тип варианта является только внутренним для модуля, я использую регулярные варианты, потому что, как вы сказали, они скомпилированы более эффективно.
  • Если тип варианта экспортируется в интерфейсе, и я чувствую, что некоторые случаи могут появляться в других модулях, но не обязательно иметь смысл сделать их зависимыми от модуля, я использую полиморфные варианты, потому что они не привязаны к модуль пространства имен. Примеры: тип тип кодирования Xmlm. Кроме того, наличие типа сигнала тип версии Uuidm. Вместо того, чтобы писать Uuidm.create Uuidm.V4, вы можете просто написать Uuidm.create 'V4, который является таким же понятным и менее подробным.
  • Иногда конкретная функция может возвращать разные случаи. Если эти случаи используются только этой функцией, я объявляю тип функции в интерфейсе без необходимости вводить определение типа. Например parse : string -> ['Error of string | 'Ok of t]
  • Полиморфные варианты и их подтипирование позволяют принудительно вводить инварианты статически с типами phantom. Кроме того, возможность их определения поэтапно может быть полезна, как для принудительного применения инвариантов статически, так и для целей документации.

Наконец, я иногда использую полиморфные варианты в реализации модуля в соответствии с 4. но без них в интерфейсе. Я препятствую этому использованию, если вы не объявляете полиморфные варианты и не закрываете их, потому что это ослабляет статическую дисциплину набора текста.

Ответ 2

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

Если бы следующее могло работать, полиморфные варианты больше не будут полезны в большинстве случаев:

type t1 = String of string | Int of int | Bool of bool | List of t1 list
type t2 = String of string | Int of int | Other

let simplify x =
  match (x : t1) with
      String s -> String s
    | Int n -> Int n
    | Bool _
    | List _ -> Other

2014-02-21 Обновление: приведенный выше код теперь действителен в OCaml 4.01. Ура!

Ответ 3

Неверно, что полиморфные варианты всегда менее эффективны. Используя пример Мартина:

type base = [`String of string | `Int of int]
type t1 = [base | `Bool of bool | `List of t1 list]
type t2 = [base | `Other]

let simplify (x:t1):t2 = match x with
| #base as b -> b
| `Bool _ | `List _ -> `Other

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

type leaf = [`String of string | `Int of int]
type 'b base = [leaf | `List of 'b list]
type t1 = [t1 base | `Bool of bool ]
type t2 = [t2 base | `Other]

let rec simplify (x:t1):t2 = match x with
| #leaf as x -> x
| `List t -> `List (List.map simplify t)
| `Bool _ -> `Other

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

К сожалению, вывод Ocaml Hindley-Milner не достаточно силен, чтобы делать такие вещи без явного набора текста, что требует тщательной факторизации типов, что, в свою очередь, затрудняет протопипирование. Кроме того, иногда требуются явные принуждения.

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