Открытые и закрытые типы объединений в Окамле - PullRequest
12 голосов
/ 27 февраля 2011

Я впервые смотрю на OCaml, немного разбираясь в F # и Haskell.Таким образом, многое выглядит знакомо, но одной вещью, которая не является, является концепция «открытых» и «закрытых» союзов (с обратным ключом и [<синтаксис).</p>

Для чего они нужны и как часто они используются?

Ответы [ 2 ]

19 голосов
/ 27 февраля 2011

Гаш ответ имеет хороший совет .Я собираюсь объяснить открытые и закрытые союзы немного подробнее.

Во-первых, вам нужно различить два вида союзов: базовые варианты (без обратного удара) и полиморфные варианты (с обратным ударением).

  • Базовые варианты являются генеративными: если вы определяете два типа с одинаковыми именами конструкторов в разных модулях M1 и M2, у вас есть разные типы.M1.Foo и M2.Foo - это разные конструкторы.`Foo всегда один и тот же конструктор, независимо от того, где вы его используете.
  • Помимо этого, полиморфные варианты могут делать все, что могут делать базовые варианты, и даже больше.Но с большой силой приходит большая сложность, поэтому вы должны использовать их только тогда, когда это необходимо и осторожно.

Полиморфный вариантный тип описывает, какие конструкторы может иметь этот тип.Но многие полиморфные вариантные типы не полностью известны - они содержат (строковые) переменные типа.Рассмотрим пустой список []: его тип - 'a list, и его можно использовать во многих контекстах, которые присваивают более конкретные типы 'a.Например:

# let empty_list = [];;
val empty_list : 'a list = []
# let list_of_lists = [] :: empty_list;;
val list_of_lists : 'a list list = [[]]
# let list_of_integers = 3 :: empty_list;;
val list_of_integers : int list = [3]

То же самое верно для переменных типа строки.Открытый тип, написанный [> … ], имеет переменную строки, которая может быть создана для покрытия большего количества конструкторов при каждом использовании значения.

# let foo = `Foo;;
val foo : [> `Foo ] = `Foo
# let bar = `Bar;;
val bar : [> `Bar ] = `Bar
# let foobar = [foo; bar];;
val foobar : [> `Bar | `Foo ] list = [`Foo; `Bar]

То, что конструктор появляется в типе, не означает, что каждыйиспользование этого типа должно разрешать все конструкторы.[> …] говорит, что тип должен иметь по крайней мере эти конструкторы, а двойные [< …] говорят, что тип должен иметь не более этих конструкторов.Рассмотрим эту функцию:

# let h = function `Foo -> `Bar | `Bar -> `Foo;;
val h : [< `Bar | `Foo ] -> [> `Bar | `Foo ] = <fun>

h может обрабатывать только Foo и Bar, поэтому тип ввода может не разрешать другие конструкторы;но можно вызывать h для типа, который допускает только Foo.И наоборот, h может возвращать Foo или Bar, и любой контекст, в котором используется h, должен разрешать как Foo, так и Bar (и может разрешать другие).

Возникают закрытые типыкогда есть соответствие минимальным и максимальным требованиям конструктора для типа.Например, давайте добавим ограничение, что h должен иметь одинаковый тип ввода и вывода:

# let hh : 'a -> 'a = function `Foo -> `Bar | `Bar -> `Foo;;
val hh : [ `Bar | `Foo ] -> [ `Bar | `Foo ] = <fun>

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

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

14 голосов
/ 27 февраля 2011

Вам необходимо прочитать «Повторное использование кода с полиморфными вариантами» Жака Гаррига:

http://www.math.nagoya -u.ac.jp / ~ Гаррик / документы / fose2000.html

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...