Закон 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)).
Если вы немного смущены этим доказательством, не волнуйтесь. Убедитесь, что вы понимаете, как устроена композиция функций. Часто вы можете интуитивно понимать, как выполняются эти законы, поскольку типы действуют как контейнеры или функции. Вы также можете просто проверить их на нескольких разных значениях типа – и сумеете с определённой долей уверенности сказать, что тип действительно подчиняется этим законам.