29

я заметил, что среди OCaml программистов я знаю, некоторые из них всегда использование полиморфных вариантов (варианты, которые не объявляются, начинающиеся с обратной кавычки), в то время как остальные никогда использование полиморфных вариантов, и предпочитают варианты заявленных типов ,Варианты или полиморфные варианты?

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

+1

Я думаю, что предпочтительным названием является «открытые варианты». – ygrek

+3

«открытый вариант» явно проще интуитивно понять, затем «полиморфный вариант». –

+0

Я думаю, что «открытый вариант» более точно относится к типам «exn». –

ответ

35

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

  1. Если тип варианта является внутренним только модулем, я использую обычные варианты, потому что, как вы сказали, что они составляются более эффективны.
  2. Если тип варианта экспортируется в интерфейсе, и я чувствую, что некоторые случаи могут появляться в других модулях, но не обязательно иметь смысл зависеть от модуля, я использую полиморфные варианты, потому что они не привязаны к модуль пространства имен. Примеры: encoding type тип Xmlm. Кроме того, наличие signal type в качестве варианта означает, что вы можете разрабатывать модули, используя ту же идею для обработки XML, не внося в зависимость от Xmlm.
  3. Если тип варианта экспортируется в интерфейсе, я считаю его иногда слишком подробным для использования регулярных вариантов, когда значения типа варианта задаются функциям модуля. Пример: version type от Uuidm. Вместо того, чтобы писать Uuidm.create Uuidm.V4, вы можете просто написать Uuidm.create `V4, который является таким же четким и менее подробным.
  4. Иногда конкретная функция может возвращать разные случаи. Если эти случаи используются только этой функцией, я объявляю тип функции в интерфейсе без необходимости вводить определение типа. Например, parse : string -> [`Error of string | `Ok of t]
  5. Полиморфные варианты и их подтипирование позволяют принудительно вводить инварианты статически с фантомными типами. Кроме того, возможность их определения поэтапно может быть полезна, как для принудительного применения инвариантов статически, так и для целей документации.

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

+1

Отличное резюме. Это должно быть в некоторых FAQ. – ygrek

+2

Это: http://stackoverflow.com/tags/ocaml/info –

11

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

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

 
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

В интересах тех, кто приходит за мной, чтобы прочитать эту тему о том, когда использовать полиморфные варианты: пример Мартина, наконец, является законным в OCaml 4.01. Радуйтесь! – maverickwoo

+0

@maverickwoo: \ O/ – aneccodeal

11

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

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 

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

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

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

Смежные вопросы