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

>ghci> yesnoIf [] "ДА!" "НЕТ!"

>"НЕТ!"

>ghci> yesnoIf [2,3,4] "ДА!" "НЕТ!"

>"ДА!"

>ghci> yesnoIf True "ДА!" "НЕТ!"

>"ДА!"

>ghci> yesnoIf (Just 500) "ДА!" "НЕТ!"

>"ДА!"

>ghci> yesnoIf Nothing "ДА!" НЕТ!"

>НЕТ!"

Класс типов Functor

Мы уже встречали множество классов типов из стандартной библиотеки. Ознакомились с классом >Ord, предусмотренным для сущностей, которые можно упорядочить. Вдоволь набаловались с классом >Eq, предназначенным для сравнения на равенство. Изучили класс >Show, предоставляющий интерфейс для типов, которые можно представить в виде строк. Наш добрый друг класс >Read помогает, когда нам надо преобразовать строку в значение некоторого типа. Ну а теперь приступим к рассмотрению класса типов >Functor, предназначенного для типов, которые могут быть отображены друг в друга.



Возможно, в этот момент вы подумали о списках: ведь отображение списков – это очень распространённая идиома в языке Haskell. И вы правы: списковый тип имеет экземпляр для класса >Functor.

Нет лучшего способа изучить класс типов >Functor, чем посмотреть, как он реализован. Вот и посмотрим:

>fmap :: (a -> b) -> f a -> f b

Итак, что у нас имеется? Класс определяет одну функцию >fmap и не предоставляет для неё реализации по умолчанию. Тип функции >fmap весьма интересен. Во всех вышеприведённых определениях классов типов тип-параметр, игравший роль типа в классе, был некоторого конкретного типа, как переменная >a в сигнатуре >(==) :: (Eq a) => a –> a –> Bool. Но теперь тип-параметр >f не имеет конкретного типа (нет конкретного типа, который может принимать переменная, например >Int, >Bool или >Maybe String); в этом случае переменная – конструктор типов, принимающий один параметр. (Напомню: выражение >Maybe Int является конкретным типом, а идентификатор >Maybe – конструктор типов с одним параметром.) Мы видим, что функция >fmap принимает функцию из одного типа в другой и функтор, применённый к одному типу, и возвращает функтор, применённый к другому типу.

Если это звучит немного непонятно, не беспокойтесь. Всё прояснится, когда мы рассмотрим несколько примеров.

Гм-м… что-то мне напоминает объявление функции >fmap! Если вы не знаете сигнатуру функции >map, вот она:

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

О, как интересно! Функция >map берёт функцию из >a в >b и список элементов типа >a и возвращает список элементов типа >b. Друзья, мы только что обнаружили функтор! Фактически функция >map – это функция >fmap, которая работает только на списках. Вот как список сделан экземпляром класса >Functor:

>instance Functor [] where

>   fmap = map

И всё! Заметьте, мы не пишем