Начнем с типа foldMap
:
foldMap :: (Foldable t, Monoid m) => (a -> m) -> t a -> m
foldMap
работает, отображая функцию a -> m
на структуру данных, а затем прогоняя ее, разбивая элементы в единое накопленное значение с помощью mappend
.
Далее, мы отмечаем, что для некоторого типа b
функции b -> b
образуют моноид с (.)
в качестве бинарной операции (т. Е. mappend
) и id
в качестве элемента идентичности (т. Е. mempty
. Если вы еще не встретили его, id
- это определяется как id x = x
). Если бы мы специализировались foldMap
на этом моноиде, мы получили бы следующий тип:
foldEndo :: Foldable t => (a -> (b -> b)) -> t a -> (b -> b)
(Я назвал функцию foldEndo
, потому что конечная функция - это функция от одного типа к одному и тому же типу.)
Теперь, если мы посмотрим на подпись списка foldr
foldr :: (a -> b -> b) -> b -> [a] -> b
мы видим, что foldEndo
совпадает с ним, за исключением обобщения на любой Foldable
и некоторого изменения порядка аргументов.
Прежде чем мы перейдем к реализации, есть техническая сложность в том, что b -> b
нельзя напрямую сделать экземпляром Monoid
. Чтобы решить эту проблему, мы используем вместо этого Endo
оболочку newtype из Data.Monoid
:
newtype Endo a = Endo { appEndo :: a -> a }
instance Monoid (Endo a) where
mempty = Endo id
Endo f `mappend` Endo g = Endo (f . g)
Написано в терминах Endo
, foldEndo
просто специализировано foldMap
:
foldEndo :: Foldable t => (a -> Endo b) -> t a -> Endo b
Итак, мы перейдем непосредственно к foldr
и определим его в терминах foldMap
.
foldr :: Foldable t => (a -> b -> b) -> b -> t a -> b
foldr f z t = appEndo (foldMap (Endo . f) t) z
Это определение по умолчанию, вы можете найти в Data.Foldable
. Вероятно, самая сложная часть - Endo . f
; если у вас возникли проблемы с этим, думайте о f
не как о бинарном операторе, а как о функции одного аргумента с типом a -> (b -> b)
; затем мы оборачиваем получившуюся конечную функцию в Endo
.
Что касается foldl
, вывод по сути такой же, за исключением того, что мы используем другой моноид эндофункций с flip (.)
в качестве бинарной операции (т.е. мы составляем функции в противоположном направлении).
person
duplode
schedule
27.04.2014
/=
, которую вы получаете, если реализуете==
, как обсуждалось здесь - person hugomg   schedule 27.04.2014