- Любовные романы
- Фантастика и фэнтези
- Ненаучная фантастика
- Ироническое фэнтези
- Научная Фантастика
- Фэнтези
- Ужасы и Мистика
- Боевая фантастика
- Альтернативная история
- Космическая фантастика
- Попаданцы
- Юмористическая фантастика
- Героическая фантастика
- Детективная фантастика
- Социально-психологическая
- Боевое фэнтези
- Русское фэнтези
- Киберпанк
- Романтическая фантастика
- Городская фантастика
- Технофэнтези
- Мистика
- Разная фантастика
- Иностранное фэнтези
- Историческое фэнтези
- LitRPG
- Эпическая фантастика
- Зарубежная фантастика
- Городское фентези
- Космоопера
- Разное фэнтези
- Книги магов
- Любовное фэнтези
- Постапокалипсис
- Бизнес
- Историческая фантастика
- Социально-философская фантастика
- Сказочная фантастика
- Стимпанк
- Романтическое фэнтези
- Ироническая фантастика
- Детективы и Триллеры
- Проза
- Юмор
- Феерия
- Новелла
- Русская классическая проза
- Современная проза
- Повести
- Контркультура
- Русская современная проза
- Историческая проза
- Проза
- Классическая проза
- Советская классическая проза
- О войне
- Зарубежная современная проза
- Рассказы
- Зарубежная классика
- Очерки
- Антисоветская литература
- Магический реализм
- Разное
- Сентиментальная проза
- Афоризмы
- Эссе
- Эпистолярная проза
- Семейный роман/Семейная сага
- Поэзия, Драматургия
- Приключения
- Детская литература
- Загадки
- Книга-игра
- Детская проза
- Детские приключения
- Сказка
- Прочая детская литература
- Детская фантастика
- Детские стихи
- Детская образовательная литература
- Детские остросюжетные
- Учебная литература
- Зарубежные детские книги
- Детский фольклор
- Буквари
- Книги для подростков
- Школьные учебники
- Внеклассное чтение
- Книги для дошкольников
- Детская познавательная и развивающая литература
- Детские детективы
- Домоводство, Дом и семья
- Юмор
- Документальные книги
- Бизнес
- Работа с клиентами
- Тайм-менеджмент
- Кадровый менеджмент
- Экономика
- Менеджмент и кадры
- Управление, подбор персонала
- О бизнесе популярно
- Интернет-бизнес
- Личные финансы
- Делопроизводство, офис
- Маркетинг, PR, реклама
- Поиск работы
- Бизнес
- Банковское дело
- Малый бизнес
- Ценные бумаги и инвестиции
- Краткое содержание
- Бухучет и аудит
- Ораторское искусство / риторика
- Корпоративная культура, бизнес
- Финансы
- Государственное и муниципальное управление
- Менеджмент
- Зарубежная деловая литература
- Продажи
- Переговоры
- Личная эффективность
- Торговля
- Научные и научно-популярные книги
- Биофизика
- География
- Экология
- Биохимия
- Рефераты
- Культурология
- Техническая литература
- История
- Психология
- Медицина
- Прочая научная литература
- Юриспруденция
- Биология
- Политика
- Литературоведение
- Религиоведение
- Научпоп
- Психология, личное
- Математика
- Психотерапия
- Социология
- Воспитание детей, педагогика
- Языкознание
- Беременность, ожидание детей
- Транспорт, военная техника
- Детская психология
- Науки: разное
- Педагогика
- Зарубежная психология
- Иностранные языки
- Филология
- Радиотехника
- Деловая литература
- Физика
- Альтернативная медицина
- Химия
- Государство и право
- Обществознание
- Образовательная литература
- Учебники
- Зоология
- Архитектура
- Науки о космосе
- Ботаника
- Астрология
- Ветеринария
- История Европы
- География
- Зарубежная публицистика
- О животных
- Шпаргалки
- Разная литература
- Зарубежная литература о культуре и искусстве
- Пословицы, поговорки
- Боевые искусства
- Прочее
- Периодические издания
- Фанфик
- Военное
- Цитаты из афоризмов
- Гиды, путеводители
- Литература 19 века
- Зарубежная образовательная литература
- Военная история
- Кино
- Современная литература
- Военная техника, оружие
- Культура и искусство
- Музыка, музыканты
- Газеты и журналы
- Современная зарубежная литература
- Визуальные искусства
- Отраслевые издания
- Шахматы
- Недвижимость
- Великолепные истории
- Музыка, танцы
- Авто и ПДД
- Изобразительное искусство, фотография
- Истории из жизни
- Готические новеллы
- Начинающие авторы
- Спецслужбы
- Подростковая литература
- Зарубежная прикладная литература
- Религия и духовность
- Старинная литература
- Справочная литература
- Компьютеры и Интернет
- Блог
Изучай Haskell во имя добра! - Миран Липовача
Шрифт:
Интервал:
Закладка:
ghci> :t fmap (*2)
fmap (*2) :: (Num a, Functor f) => f a –> f a
ghci> :t fmap (replicate 3)
fmap (replicate 3) :: (Functor f) => f a –> f [a]
Выражение fmap (*2) – это функция, которая получает функтор f над числами и возвращает функтор над числами. Таким функтором могут быть список, значение Maybe, Either String или что-то другое. Выражение fmap (replicate 3) получит функтор над любым типом и вернёт функтор над списком элементов данного типа. Это становится ещё очевиднее, если мы частично применим, скажем, fmap (++"!"), а затем привяжем её к имени в GHCi.
Вы можете рассматривать fmap двояко:
• как функцию, которая принимает функцию и значение функтора, а затем отображает это значение функтора с помощью данной функции;
• как функцию, которая принимает функцию и втягивает её в функтор, так чтобы она оперировала значениями функторов.
Обе точки зрения верны.
Тип fmap (replicate 3) :: (Functor f) => f a –> f [a] означает, что функция будет работать с любым функтором. Что именно она будет делать, зависит от функтора. Если мы применим fmap (replicate 3) к списку, будет выбрана реализация fmap для списка, то есть просто map. Если мы применим её к Maybe a, она применит replicate 3 к значению внутри Just. Если это значение равно Nothing, то оно останется равным Nothing. Вот несколько примеров:
ghci> fmap (replicate 3) [1,2,3,4]
[[1,1,1],[2,2,2],[3,3,3],[4,4,4]]
ghci> fmap (replicate 3) (Just 4)
Just [4,4,4]
ghci> fmap (replicate 3) (Right "ля")
Right ["ля","ля","ля"]
ghci> fmap (replicate 3) Nothing
Nothing
ghci> fmap (replicate 3) (Left "фуу")
Left "фуу"
Законы функторов
Предполагается, что все функторы проявляют определённые свойства и поведение. Они должны надёжно вести себя как сущности, которые можно отобразить. Применение функции fmap к функтору должно только отобразить функтор с помощью функции – ничего более. Это поведение описано в законах функторов. Все экземпляры класса Functor должны следовать этим двум законам. Язык Haskell не принуждает, чтобы эти законы выполнялись автоматически, поэтому вы должны проверять их сами, когда создаёте функтор. Все экземпляры класса Functor в стандартной библиотеке выполняют эти законы.
Закон 1
Первый закон функторов гласит, что если мы применяем функцию id к значению функтора, то значение функтора, которое мы получим, должно быть таким же, как первоначальное значение функтора. В формализованной записи это выглядит так: fmap id = id. Иными словами, если мы применим fmap id к значению функтора, это должно быть то же самое, что и просто применение функции id к значению. Вспомните, что id – это функция тождества, которая просто возвращает свой параметр неизменным. Она также может быть записана в виде x –> x. Если воспринимать значение функтора как нечто, что может быть отображено, то закон fmap id = id представляется довольно очевидным.
Давайте посмотрим, выполняется ли он для некоторых значений функторов:
ghci> fmap id (Just 3)
Just 3
ghci> id (Just 3)
Just 3
ghci> fmap id [1..5]
[1,2,3,4,5]
ghci> id [1..5]
[1,2,3,4,5]
ghci> fmap id []
[]
ghci> fmap id Nothing
Nothing
Если посмотреть на реализацию функцию fmap, например, для типа Maybe, мы можем понять, почему выполняется первый закон функторов:
instance Functor Maybe where
fmap f (Just x) = Just (f x)
fmap f Nothing= Nothing
Мы представляем, что функция id играет роль параметра f в этой реализации. Нам видно, что если мы применяем fmap id к значению Just x, то результатом будет Just (id x), и поскольку id просто возвращает свой параметр, мы можем сделать вывод, что Just (id x) равно Just x. Теперь нам известно, что если мы применим функцию id к значению типа Maybe, созданному с помощью конструктора данных Just, обратно мы получим то же самое значение.
Видно, что применение функции id к значению Nothing возвращает то же самое значение Nothing. Поэтому из этих двух равенств в реализации функции fmap нам видно, что закон fmap id = id соблюдается.
Закон 2
Второй закон гласит, что композиция двух функций и последующее применение результирующей функции к функтору должны давать тот же результат, что и применение первой функции к функтору, а затем применение другой. В формальной записи это выглядит так: fmap (f . g) = fmap f . fmap g. Или если записать по-другому, то для любого значения функтора x должно выполняться следующее: fmap (f . g) x = fmap f (fmap g x).
Если мы выявили, что некоторый тип подчиняется двум законам функторов, надо надеяться, что он обладает такими же фундаментальными поведениями, как и другие функторы, когда дело доходит до отображения. Мы можем быть уверены, что когда мы применяем к нему функцию fmap, за кулисами ничего не произойдёт, кроме отображения, и он будет действовать как сущность, которая может быть отображена – то есть функтор.
Можно выяснить, как второй закон выполняется по отношению к некоторому типу, посмотрев на реализацию функции fmap для этого типа, а затем использовав метод, который мы применяли, чтобы проверить, подчиняется ли тип Maybe первому закону. Итак, чтобы проверить, как второй закон функторов выполняется для типа Maybe, если мы применим выражение fmap (f . g) к значению Nothing, мы получаем то же самое значение Nothing, потому что применение любой функции к Nothing даёт Nothing. Если мы выполним выражение fmap f (fmap g Nothing), то получим результат Nothing по тем же причинам.
Довольно просто увидеть, как второй закон выполняется для типа Maybe, когда значение равно Nothing. Но что если это значение Just? Ладно – если мы выполним fmap (f . g) (Just x), из реализации нам будет видно, что это реализовано как Just ((f . g) x); аналогичной записью было бы Just (f (g x)). Если же мы выполним fmap f (fmap g (Just x)), то из реализации увидим, что fmap g (Just x) – это Just (g x). Следовательно, fmap f (fmap g (Just x)) равно fmap f (Just (g x)), а из реализации нам видно, что это равнозначно Just (f (g x)).
Если вы немного смущены этим доказательством, не волнуйтесь. Убедитесь, что вы понимаете, как устроена композиция функций. Часто вы можете интуитивно понимать, как выполняются эти законы, поскольку типы действуют как контейнеры или функции. Вы также можете просто проверить их на нескольких разных значениях типа – и сумеете с определённой долей уверенности сказать, что тип действительно подчиняется этим законам.
Нарушение закона
Давайте посмотрим на «патологический» пример конструктора типов, который является экземпляром класса типов Functor, но не является функтором, потому что он не выполняет законы. Скажем, у нас есть следующий тип:
data CMaybe a = CNothing | CJust Int a deriving (Show)
Буква C здесь обозначает счётчик. Это тип данных, который во многом похож на тип Maybe a, только часть Just содержит два поля вместо одного. Первое поле в конструкторе данных CJust всегда имеет тип Int; оно будет своего рода счётчиком. Второе поле имеет тип a, который берётся из параметра типа, и его тип будет зависеть от конкретного типа, который мы выберем для CMaybe a. Давайте поэкспериментируем с нашим новым типом:
ghci> CNothing
CNothing
ghci> CJust 0 "ха-ха"
CJust 0 "ха-ха"
ghci> :t CNothing
CNothing :: CMaybe a
ghci> :t CJust 0 "ха-ха"
CJust 0 "ха-ха" :: CMaybe [Char]
ghci> CJust 100 [1,2,3]
CJust 100 [1,2,3]
Если мы используем конструктор данных CNothing, в нём нет полей. Если мы используем конструктор данных CJust, первое поле является целым числом, а второе может быть любого типа. Давайте сделаем этот тип экземпляром класса Functor, так чтобы каждый раз, когда мы используем функцию fmap, функция применялась ко второму полю, а первое поле увеличивалось на 1:
instance Functor CMaybe where
fmap f CNothing= CNothing
fmap f (CJust counter x) = CJust (counter+1) (f x)
Это отчасти похоже на реализацию экземпляра для типа Maybe, только когда функция fmap применяется к значению, которое не представляет пустую коробку (значение CJust), мы не просто применяем функцию к содержимому, но и увеличиваем счётчик на 1. Пока вроде бы всё круто! Мы даже можем немного поиграть с этим:

