Повторное использование и расширение определенного типа в Ocaml

В Ocaml существует ли простая конструкция/стиль для расширения определенного типа?

Скажем, если у нас есть булев тип

bool2 = True | False 

Теперь мы хотим расширить его для 3-значной логики. В Ocaml есть более элегантный, чем переопределение bool2:

bool3 = True | False | ThirdOne

Ответ 1

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

Я бы посоветовал вернуться и изменить старый код, как это кажется естественным. Если вы написали свой код с учетом расширяемости и, в частности, избегали _ -patterns по типу bool2, тогда компилятор предупредит вас о любом месте, где сделано предположение о наличии только двух конструкторов. Эта компиляторная обратная связь по модификации типов очень полезна, так как это механическая помощь, чтобы сделать вашу программу правильной.

Этот способ делать вещи имеет, конечно, некоторые недостатки. Один из них заключается в том, что изменение определения типа, тогда изменение каждого варианта использования может не сработать с вашей обычной практикой компиляции: если вы сделаете это на большой базе кода, у вас будет большое количество вещей, которые нужно выполнить перед компиляцией вашего проекта (и, следовательно, можно протестировать). Вы можете разделить свою модификацию на несколько патчей на свою систему контроля версий, но это означает, что некоторые посреднические совершенные состояния не компилируются, что не очень приятно. Другая возможность заключается в том, чтобы изменить это место только для добавления сбоя во время выполнения (| Third_one -> assert false), тогда у вас есть компилируемый код, и вы можете исправить эти сбои, как это происходит во время выполнения во время тестирования приложения. Но я по-прежнему думаю, что статическая компиляция обратной связи - хорошая помощь для обслуживания кода.

Существует также возможность обертывания алгебраического типа данных в "расширенном алгебраическом типе данных" type bool3 = New | Old of bool2, который обсуждается в ссылке, которую вы даете в качестве комментария ответа Мартина. Это может быть хорошим способом перехода от одного типа данных к другому без нарушения компиляции, но в долгосрочной перспективе это болезненно, особенно если вы складываете больше этих расширений друг на друга.

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

PS: в отношении полиморфных вариантов и их связи с проблемой выражения обязательная статья Повторное использование кода через полиморфные варианты Жака Гаррига.

Ответ 2

Полиморфные варианты обеспечивают эту функциональность:

type bool2 = [ `True | `False ]
type bool3 = [ bool2 | `Third_one ]

Что это.

Существует другой полезный ярлык для полиморфных вариантов. В сопоставлении с образцом используйте имя типа, которому предшествует #:

let is_bool2 = function
    #bool2 -> true
  | `Third_one -> false

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

type bool3 = Bool of bool | Third_one

При сопоставлении с образцом компилятор проверяет, охвачены ли все случаи, не требуя аннотаций типа. Это выглядит так:

match x with
| Bool true -> ...
| Bool false -> ...
| Third_one -> ...

Ответ 3

В зависимости от задачи вы также можете найти Extensible Variant Types. Дополнительная информация и примеры использования здесь.

type bool = ..
type bool +=
    | True
    | False

(* Elsewhere *)
type bool +=
    | Third_one