Какие языки программирования имеют что-то вроде Haskells `newtype`
Язык программирования Haskell имеет концепцию newtypes
: если я пишу newtype Foo = Foo (Bar)
, то создается новый тип Foo
, который изоморфен Bar
, то есть есть взаимно однозначные преобразования между ними. Свойства этой конструкции:
- Два типа полностью разделены (т.е. компилятор не позволит вам использовать тот, где ожидается другой, без использования явных преобразований).
- Они имеют одинаковое представление. В частности, функции преобразования имеют нулевую временную стоимость и возвращают "тот же объект" в кучу.
- Конверсия возможна только между этими типами и не может быть использована неправильно, т.е. сохраняется безопасность типа.
Какие другие языки программирования предоставляют эту функцию?
Один пример, похоже, является однозначной структурой в C, когда используется только с элементами доступа к записи/конструкторами. Недействительные кандидаты были бы однозначными -структурами в C, когда они использовались с кастами, поскольку приведение не проверяется компилятором или объектами с одним членом на Java, поскольку они не будут иметь одинаковое представление.
Похожие вопросы: Есть ли у F # новый тип Haskell? (Нет) и Есть ли у D новый тип? (не более).
Ответы
Ответ 1
Frege имеет это, хотя, в отличие от Haskell, нет лишнего ключевого слова. Вместо этого каждый тип продукта с одним компонентом является новым типом.
Пример:
data Age = Age Int
Кроме того, все функции, которые имеют номинальную типизацию и позволяют определять тип в терминах другого, должны иметь эту функцию. Например, Oberon, Modula-2 или ADA. Итак, после
type age = integer; {* kindly forgive syntax errors *}
нельзя было путать возраст и некоторую другую величину.
Ответ 2
Я полагаю, что классы значений удовлетворяют этим условиям.
Например:
case class Kelvin(k: Double) extends AnyVal
Изменить: на самом деле, я не уверен, что конверсии имеют нулевые накладные расходы во всех случаях. В этой документации описаны некоторые случаи, когда требуется выделение объектов в куче, поэтому я предполагаю, что в таких случаях для доступа к базовому значению от объекта будут некоторые служебные данные во время выполнения.
Ответ 3
Go имеет следующее:
Если мы объявим
type MyInt int
var i int
var j MyInt
то я имеет тип int и j имеет тип MyInt. Переменные я и j имеют разные статические типы и, хотя они имеют один и тот же базовый тип, они не могут быть назначены друг другу без преобразования.
"Тот же самый базовый тип" означает, что представление в памяти MyInt
является точно таким же, как и для int
. Передача MyInt
функции, ожидающей int
, является ошибкой времени компиляции. То же самое верно для композитных типов, например. после
type foo struct { x int }
type bar struct { x int }
вы не можете передать bar
функции, ожидающей foo
(test).
Ответ 4
Mercury - это чистый логический язык программирования с системой типов, подобной системе Haskell.
Оценка в ртути строгая, а не ленивая, поэтому не было бы семантической разницы между эквивалентами ртути newtype
и data
. Следовательно, любой тип, который имеет только один конструктор с одним аргументом, представляется идентично типу этого аргумента, но все же рассматривается как один и тот же тип; Эффект "newtype" - это прозрачная оптимизация в Mercury. Пример:
:- type wrapped
---> foo(int)
; bar(string).
:- type wrapper ---> wrapper(wrapped).
:- type synonym == wrapped.
Представление wrapper
будет идентично представлению wrapped
, но это отдельный тип, в отличие от synonym
, который является просто другим именем для типа wrapped
.
Меркурий использует помеченные указатели в своих представлениях. 1 Будучи строгим и позволяющим иметь разные представления для разных типов, Меркурий обычно пытается покончить с боксом, где это возможно. например.
- Чтобы ссылаться на значение типа "enum-like" (все конструкторы с нулевым числом), вам не нужно указывать на какую-либо память, чтобы вы могли использовать целое слово бит бита, чтобы сказать, какой конструктор он и встроен что в ссылке
- Чтобы ссылаться на список, вы можете использовать тег с указателем на ячейку cons (а не указатель на структуру, которая сама содержит информацию о том, является ли она нилой или ячейкой cons)
- и т.д.
Оптимизация "нового типа" - это действительно одно конкретное приложение этой общей идеи. Тип "обертка" не нуждается в ячейках памяти, выделенных выше того, что уже держит "завернутый" тип. И так как ему нужны нулевые биты тегов, он также может вставлять любые теги в ссылку на "завернутый" тип. Поэтому вся ссылка на "завернутый" тип может быть встроена в ссылку на тип обертки, которая в процессе выполнения становится неотличимой.
1 Детали здесь могут применяться только к классам компиляции низкого уровня. Меркурий также может скомпилироваться с "высоким уровнем" C или Java. Очевидно, что в Java нет бит-скриптов (хотя, насколько я знаю, оптимизация "newtype" по-прежнему применяется), и я чуть менее знаком с деталями реализации в классах высокого уровня C.
Ответ 5
Rust всегда позволял вам создавать однополевые типы, но с недавно стабилизированным атрибутом repr(transparent)
вы теперь можете быть уверены, что созданный тип будет иметь точную компоновку данных в виде обернутого типа, даже в FFI и так далее.
#[repr(transparent)]
pub struct FooWrapper(Foo);