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

И прежде всего давайте взглянем на ключевое слово >newtype: мы будем часто его использовать, когда углубимся в удивительный мир моноидов.

Оборачивание существующего типа в новый тип

Пока что вы научились создавать свои алгебраические типы данных, используя ключевое слово >data. Вы также увидели, как можно давать синонимы имеющимся типам с применением ключевого слова >type. В этом разделе мы рассмотрим, как создаются новые типы на основе имеющихся типов данных с использованием ключевого слова >newtype. И в первую очередь, конечно, поговорим о том, чем всё это может быть нам полезно.

В главе 11 мы обсудили пару способов, при помощи которых списковый тип может быть аппликативным функтором. Один из этих способов состоит в том, чтобы заставить оператор ><*> брать каждую функцию из списка, являющегося его левым параметром, и применять её к каждому значению в списке, который находится справа, что в результате возвращает все возможные комбинации применения функции из левого списка к значению в правом:

>ghci> [(+1),(*100),(*5)] <*> [1,2,3]

>[2,3,4,100,200,300,5,10,15]

Второй способ заключается в том, чтобы взять первую функцию из списка слева от оператора ><*> и применить её к первому значению справа, затем взять вторую функцию из списка слева и применить её ко второму значению справа, и т. д. В конечном счёте получается нечто вроде застёгивания двух списков.

Но списки уже имеют экземпляр класса >Applicative, поэтому как нам определить для списков второй экземпляр класса >Applicative? Как вы узнали, для этой цели был введён тип >ZipList a. Он имеет один конструктор данных >ZipList, у которого только одно поле. Мы помещаем оборачиваемый нами список в это поле. Далее для типа >ZipList определяется экземпляр класса >Applicative, чтобы, когда нам понадобится использовать списки в качестве аппликативных функторов для застёгивания, мы могли просто обернуть их с по мощью конструктора >ZipList. Как только мы закончили, разворачиваем их с помощью >getZipList:

>ghci> getZipList $ ZipList [(+1),(*100),(*5)] <*> ZipList [1,2,3]

>[2,200,15]

Итак, какое отношение это имеет к ключевому слову >newtype? Хорошо, подумайте, как бы мы могли написать объявление >data для нашего типа >ZipList a! Вот один из способов:

>data ZipList a = ZipList [a]

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