Варианты или полиморфные варианты?
Я заметил, что среди программистов 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 не достаточно силен, чтобы делать такие вещи без явного набора текста, что требует тщательной факторизации типов, что, в свою очередь, затрудняет протопипирование. Кроме того, иногда требуются явные принуждения.
Большой недостаток этой методики заключается в том, что для терминов с несколькими параметрами вскоре заканчивается довольно запутанный комбинаторный взрыв типов, и, в конце концов, легче отказаться от статического принуждения и использовать тип кухонной раковины с подстановочные знаки и исключения (например, динамическая типизация).