<$>
(он же fmap
) является членом класса Functor следующим образом:
class Functor f where
fmap :: (a -> b) -> f a -> f b
Так, что бы f ни было параметризованным типом с одним аргументом типа. Списки являются одним из таких типов, когда они записаны в префиксной форме []
([] a
совпадает с [a]
). Таким образом, экземпляр для списков:
instance Functor [] where
-- fmap :: (a -> b) -> [] a -> [] b
fmap = map
Пары также можно записать в виде префикса: (,) a b
совпадает с (a, b)
. Итак, давайте рассмотрим, что мы делаем, если нам нужен экземпляр Functor, включающий пары. Мы не можем объявить instance Functor (,)
, потому что конструктор пары (,)
принимает два типа - и они могут быть разных типов! Что мы можем сделать, так это объявить экземпляр для (,) a
- это тип, которому нужен только еще один тип:
instance Functor ( (,) a ) where
-- fmap :: (b -> c) -> (,) a b -> (,) a c
fmap f (x, y) = (x, f y)
Надеюсь, вы видите, что определение fmap - единственное разумное определение, которое мы можем дать. Ответ относительно того, почему экземпляр functor работает со вторым элементом в паре, заключается в том, что тип для второго элемента является последним в списке! Мы не можем легко объявить экземпляр функтора, который работает с первым элементом в паре. Между прочим, это обобщается на более крупные кортежи, например четверка (,,,) a b c d
(он же (a, b, c, d)
) также может иметь экземпляр Functor
для последнего элемента:
instance Functor ( (,,,) a b c) where
-- fmap :: (d -> e) -> (,,,) a b c d -> (,,,) a b c e
fmap f (p, q, r, s) = (p, q, r, f s)
Надеюсь, это поможет объяснить все это!