Изучай Haskell во имя добра! (Липовача) - страница 199

:

>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 = "Вот что мы покажем!"}