Отличный вопрос!
Существует несколько ключевых отличий.
Представление
- A
newtype
гарантирует, что ваши данные будут иметьточно такое же представление во время выполнения, как и тип, который вы переносите. - Хотя
data
объявляет совершенно новую структуру данных во время выполнения.
Таким образом, ключевой момент здесь заключается в том, чтоКонструкция для newtype
гарантированно будет стерта во время компиляции.
Примеры:
newtype Book = Book (Int, Int)
Обратите внимание, что он имеет точно такое же представление, что и (Int,Int)
, поскольку конструктор Book
удален.
data Book = Book (Int, Int)
Имеет дополнительный конструктор Book
, отсутствующий в newtype
.
data Book = Book {-# UNPACK #-}!Int {-# UNPACK #-}!Int
Нет указателей!Два поля Int
- это поля без размера в конструкторе Book
.
Алгебраические типы данных
Из-за этого необходимо удалить конструктор, anewtype
работает только при переносе типа данных с одним конструктором .Там нет понятия "алгебраических" новых типов.То есть вы не можете написать эквивалент нового типа, скажем,
data Maybe a = Nothing
| Just a
, поскольку он имеет более одного конструктора.Вы также не можете написать
newtype Book = Book Int Int
Строгость
Тот факт, что конструктор удален, приводит к некоторым очень тонким различиям в строгости между data
и newtype
.В частности, data
вводит тип, который «поднимается», что означает, по сути, что у него есть дополнительный способ оценки до нижнего значения.Поскольку во время выполнения нет дополнительного конструктора с newtype
, это свойство не сохраняется.
Этот дополнительный указатель в конструкторе Book
to (,)
позволяет нам ввести нижнее значение.
В результате newtype
и data
имеют немного разные свойства строгости, как объяснено в вики-статье на Haskell .
Распаковка
Не имеет смысла распаковывать компоненты newtype
,так как нет конструктора.В то время как вполне разумно написать:
data T = T {-# UNPACK #-}!Int
, получая объект времени выполнения с конструктором T
и компонентом Int#
.Вы просто получаете Int
с newtype
.
Ссылки :