"Подчеркивают ли типы подчеркивания OCaml (например," _a ") вероятность ошибок/нарушений работоспособности во время выполнения?

Я немного читал об ограничении значения в стандартном ML и пытался перевести пример в OCaml, чтобы посмотреть, что он будет делать. Кажется, что OCaml создает эти типы в контекстах, где SML отклоняет программу из-за ограничения значения. Я также видел их в других контекстах, таких как пустые хеш-таблицы, которые еще не были "специализированными" для определенного типа.

http://mlton.org/ValueRestriction

Вот пример отклоненной программы в SML:

val r: 'a option ref = ref NONE
val r1: string option ref = r
val r2: int option ref = r
val () = r1 := SOME "foo"
val v: int = valOf (!r2)

Если вы введете первую строку дословно в SML из Нью-Джерси, вы получите следующая ошибка:

- val r: 'a option ref = ref NONE;
stdIn:1.6-1.33 Error: explicit type variable cannot be generalized at its binding declaration: 'a

Если вы оставляете аннотацию явного типа, вы получаете

- val r = ref NONE

stdIn:1.6-1.18 Warning: type vars not generalized because of
   value restriction are instantiated to dummy types (X1,X2,...)
val r = ref NONE : ?.X1 option ref

В чем именно этот фиктивный тип? Кажется, он полностью недоступен и не может объединиться ни с чем.

- r := SOME 5;

stdIn:1.2-1.13 Error: operator and operand don't agree [overload conflict]
  operator domain: ?.X1 option ref * ?.X1 option
  operand:         ?.X1 option ref * [int ty] option
  in expression:
    r := SOME 5

В OCaml, напротив, переменная типа "фиктивный тип" доступна и объединяется с первой, что она может.

# let r : 'a option ref = ref None;;
val r : '_a option ref = {contents = None}

# r := Some 5;;
- : unit = ()
# r ;;
- : int option ref = {contents = Some 5}

Это несколько запутывает и вызывает несколько вопросов.

1) Возможно ли, что соответствующая реализация SML позволяет сделать тип "dummy" выше доступным?

2) Как OCaml сохраняет надежность без ограничения значения? Предоставляет ли он более слабые гарантии, чем SML?

3) Тип '_a option ref кажется менее полиморфным, чем 'a option ref. Почему в OCaml не отклоняется let r : 'a option ref = ref None;; (с явной аннотацией)?

Ответ 1

1) Может ли соответствующая реализация SML выбрать доступный тип "dummy" выше?

В пересмотренном определении (SML97) не указывается, что существует тип "dummy"; все это формально указывает, что val не может вводить переменную полиморфного типа, поскольку правое выражение не является нерасширяющим выражением. (Есть также некоторые комментарии о переменных типа, которые не просачиваются на верхний уровень, но, как указывает Андреас Росберг в своих дефектах в пересмотренном определении стандартного ML, эти комментарии действительно о неопределенных типах, а не о переменных типа, которые появляются в формализме определения, поэтому они не могут быть действительно приняты как часть требований.)

На практике я думаю, что есть четыре подхода, которые реализуют:

  • некоторые реализации отклоняют затронутые объявления во время проверки типов и заставляют программиста указывать мономорфный тип.
  • некоторые реализации, такие как MLton, предотвращают обобщение, но откладывают унификацию, так что соответствующий мономорфный тип может стать ясным позже в программе.
  • SML/NJ, как вы видели, выдает предупреждение и создает экземпляр фиктивного типа, который впоследствии не может быть унифицирован ни с каким другим типом.
  • Я думаю, что я слышал, что некоторые версии по умолчанию имеют значение int? Я не уверен.

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

2) Как OCaml сохраняет надежность без ограничения значения? Предоставляет ли он более слабые гарантии, чем SML?

Я не очень хорошо знаком с OCaml, но из того, что вы пишете, похоже, что он использует тот же подход, что и MLton; поэтому он не должен жертвовать своей надежностью.

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

3) Тип '_a option ref кажется менее полиморфным, чем 'a option ref. Почему в OCaml не отклоняется let r : 'a option ref = ref None;; (с явным аннотацией)?

Опять же, я не очень хорошо знаком с OCaml, но... да, это мне кажется ошибкой!

Ответ 2

Слабо полиморфные типы (типы '_) - это удобство программирования, а не истинное расширение системы типов.

2) Как OCaml сохраняет надежность без ограничения значения? Предоставляет ли он более слабые гарантии, чем SML?

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

Error: The type of this expression, '_a option ref,
       contains type variables that cannot be generalized

3) Тип '_a ref ref кажется менее полиморфным, чем' вариант ref. Почему не допускается r: 'опция ref = ref None;; (с явным аннотацией), отклоненным в OCaml?

Это потому, что '_a не является "реальным" типом, например, запрещено писать подпись, явно определяющую значения этого "типа":

# module A : sig val table : '_a option ref end = struct let option = ref None end;;
Characters 27-30:
  module A : sig val table : '_a option ref end = struct let option = ref None end;;
                             ^^^
Error: The type variable name '_a is not allowed in programs

Можно избежать использования этих слабо полиморфных типов, используя рекурсивные объявления, чтобы собрать вместе декларацию слабо полиморфной переменной и более позднюю функцию, которая завершает определение типа, например:

# let rec r = ref None and set x = r := Some(x + 1);;
val r : int option ref = {contents = None}
val set : int -> unit = <fun>

Ответ 3

Чтобы ответить на вторую часть вашего последнего вопроса,

3) [...] Почему не допускается r: 'a option ref = ref None;; (с явным аннотацией), отклоненным в OCaml?

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

let n : 'a = 5

полностью действует в OCaml. Возможно, это скорее вводит в заблуждение, а не лучший выбор дизайна.

Чтобы обеспечить полиморфизм в OCaml, вам нужно написать что-то вроде

let n : 'a. 'a = 5

что действительно вызовет ошибку. Однако это вводит локальные кванторы, поэтому все еще несколько отличается от SML и не работает для примеров, где 'a необходимо связать в другом месте, например. следующее:

fun pair (x : 'a) (y : 'a) = (x, y)

В OCaml вам нужно переписать это на

let pair : 'a. 'a -> 'a -> 'a * 'a = fun x y -> (x, y)