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

Поскольку существует так много структур данных, которые хорошо работают со свёртками, был введён класс типов >Foldable. Подобно тому как класс >Functor предназначен для сущностей, которые можно отображать, класс >Foldable предназначен для вещей, которые могут быть свёрнуты! Его можно найти в модуле >Data.Foldable; и, поскольку он экспортирует функции, имена которых конфликтуют с именами функций из модуля >Prelude, его лучше импортировать, квалифицируя (и подавать с базиликом!):

>import qualified Data.Foldable as F

Чтобы сэкономить драгоценные нажатия клавиш, мы импортировали его, квалифицируя как >F.

Так какие из некоторых функций определяет этот класс типов? Среди них есть функции >foldr, >foldl, >foldr1 и >foldl1. Ну и?.. Мы уже давно знакомы с ними! Что ж в этом нового? Давайте сравним типы функции >foldr из модуля >Foldable и одноимённой функции из модуля >Prelude, чтобы узнать, чем они отличаются:

>ghci> :t foldr

>foldr :: (a –> b –> b) –> b –> [a] –> b

>ghci> :t F.foldr

>F.foldr :: (F.Foldable t) => (a –> b –> b) –> b –> t a –> b

А-а-а! Значит, в то время как функция >foldr принимает список и сворачивает его, функция >foldr из модуля >Data.Foldable принимает любой тип, который можно свернуть, – не только списки! Как и ожидалось, обе функции >foldr делают со списками одно и то же:

>ghci> foldr (*) 1 [1,2,3]

>6

>ghci> F.foldr (*) 1 [1,2,3]

>6

Другой структурой данных, поддерживающей свёртку, является >Maybe, которую мы все знаем и любим!

>ghci> F.foldl (+) 2 (Just 9)

>11

>ghci> F.foldr (||) False (Just True)

>True

Но сворачивание значения >Maybe не очень-то интересно. Оно действует просто как список с одним элементом, если это значение >Just, и как пустой список, если это значение >Nothing. Давайте рассмотрим чуть более сложную структуру данных.

Помните древовидную структуру данных из главы 7? Мы определили её так:

>data Tree a = EmptyTree | Node a (Tree a) (Tree a) deriving (Show)

Вы узнали, что дерево – это либо пустое дерево, которое не содержит никаких значений, либо узел, который содержит одно значение, а также два других дерева. После того как мы его определили, мы сделали для него экземпляр класса >Functor, и это дало нам возможность отображать его с помощью функций, используя функцию >fmap. Теперь мы определим для него экземпляр класса >Foldable, чтобы у нас появилась возможность производить его свёртку.

Один из способов сделать для конструктора типа экземпляр класса >Foldable состоит в том, чтобы просто напрямую реализовать для него функцию >foldr. Но другой, часто более простой способ состоит в том, чтобы реализовать функцию