:
>data ZipList a = ZipList { getZipList :: [a] }
Это прекрасно смотрится и на самом деле работает очень хорошо. У нас было два способа сделать существующий тип экземпляром класса типов, поэтому мы использовали ключевое слово >data
, чтобы просто обернуть этот тип в другой, и сделали другой тип экземпляром вторым способом.
Ключевое слово >newtype
в языке Haskell создано специально для тех случаев, когда мы хотим просто взять один тип и обернуть его во что-либо, чтобы представить его как другой тип. В существующих сейчас библиотеках тип >ZipList
>a
определён вот так:
>newtype ZipList a = ZipList { getZipList :: [a] }
Вместо ключевого слова >data
используется >newtype
. Теперь разберёмся, почему. Ну, к примеру, декларация >newtype
быстрее. Если вы используете ключевое слово >data
для оборачивания типа, появляются «накладные расходы» на все эти оборачивания и разворачивания, когда ваша программа выполняется. Но если вы воспользовались ключевым словом >newtype
, язык Haskell знает, что вы просто применяете его для оборачивания существующего типа в новый тип (отсюда название), поскольку хотите, чтобы внутренне он остался тем же, но имел иной тип. По этой причине язык Haskell может избавиться от оборачивания и разворачивания, как только решит, какое значение какого типа.
Так почему бы всегда не использовать >newtype
вместо >data
? Когда вы создаёте новый тип из имеющегося типа, используя ключевое слово >newtype
, у вас может быть только один конструктор значения, который имеет только одно поле. Но с помощью ключевого слова >data
вы можете создавать типы данных, которые имеют несколько конструкторов значения, и каждый конструктор может иметь ноль или более полей:
>data Profession = Fighter | Archer | Accountant
>data Race = Human | Elf | Orc | Goblin
>data PlayerCharacter = PlayerCharacter Race Profession
При использовании ключевого слова >newtype
мы можем использовать ключевое слово >deriving
– точно так же, как мы бы делали это с декларацией >data
. Мы можем автоматически порождать экземпляры для классов >Eq
, >Ord
, >Enum
, >Bounded
, >Show
и >Read
. Если мы породим экземпляр для класса типа, то оборачиваемый нами тип уже должен иметь экземпляр для данного класса типов. Это логично, поскольку ключевое слово >newtype
всего лишь оборачивает существующий тип. Поэтому теперь мы сможем печатать и сравнивать значения нашего нового типа, если сделаем следующее:
>newtype CharList = CharList { getCharList :: [Char] } deriving (Eq, Show)
Давайте попробуем:
>ghci> CharList "Вот что мы покажем!"
>CharList {getCharList = "Вот что мы покажем!"}