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

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

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

3 ответа

Решение

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

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

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

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

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

тип t1 = строка строки | Int of int | Bool of bool | Список списка t1
тип t2 = строка строки | Int of int | Другой

позвольте упростить х =
  сопоставить (x: t1) с
      Строка s -> Строка s
    | Int n -> Int n
    | Bool _
    | Список _ -> Другое

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

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

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

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

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

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

Другие вопросы по тегам