Ответ 1
Если я правильно понимаю ваши примеры, типы ai -> b -> ai
, а не ai -> b -> a
, как вы впервые писали. Перепишите типы на a -> ri -> ri
, просто потому, что это помогает мне думать.
Первое, что нужно наблюдать, это соответствие:
(a -> r1 -> r1, ..., a -> rn -> rn) ~ a -> (r1 -> r1, ..., rn -> rn)
Это позволяет вам записать эти две функции, которые являются обратными:
pullArg :: (a -> r1 -> r1, a -> r2 -> r2) -> a -> (r1 -> r1, r2 -> r2)
pullArg (f, g) = \a -> (f a, g a)
pushArg :: (a -> (r1 -> r1, r2 -> r2)) -> (a -> r1 -> r1, a -> r2 -> r2)
pushArg f = (\a -> fst (f a), \a -> snd (f a))
Второе наблюдение: типы формы ri -> ri
иногда называются эндоморфизмами, и каждый из этих типов имеет моноид с композицией как ассоциативную операцию и тождественную функцию как тождество. Пакет Data.Monoid
имеет эту оболочку для этого:
newtype Endo a = Endo { appEndo :: a -> a }
instance Monoid (Endo a) where
mempty = id
mappend = (.)
Это позволяет вам переписать предыдущий pullArg
на это:
pullArg :: (a -> r1 -> r1, a -> r2 -> r2) -> a -> (Endo r1, Endo r2)
pullArg (f, g) = \a -> (Endo $ f a, Endo $ g a)
Третье наблюдение: произведение двух моноидов также является моноидом, как в этом случае также из Data.Monoid
:
instance (Monoid a, Monoid b) => Monoid (a, b) where
mempty = (mempty, mempty)
(a, b) `mappend` (c, d) = (a `mappend` c, b `mappend d)
Аналогично для кортежей любого числа аргументов.
Четвертое наблюдение: Что такое складки? Ответ: складки сделанные из моноидов!
import Data.Monoid
fold :: Monoid m => (a -> m) -> [a] -> m
fold f = mconcat . map f
Этот fold
является просто специализацией foldMap
из Data.Foldable
, поэтому на самом деле нам не нужно его определять, мы можем просто импортировать его более общую версию:
foldMap :: (Foldable t, Monoid m) => (a -> m) -> t a -> m
Если вы fold
с Endo
, это то же самое, что и сгибание справа. Чтобы свернуть слева, вы хотите сбросить с помощью моноида Dual (Endo r)
:
myfoldl :: (a -> Dual (Endo r)) -> r -> -> [a] -> r
myfoldl f z xs = appEndo (getDual (fold f xs)) z
-- From `Data.Monoid`. This just flips the order of `mappend`.
newtype Dual m = Dual { getDual :: m }
instance Monoid m => Monoid (Dual m) where
mempty = Dual mempty
Dual a `mappend` Dual b = Dual $ b `mappend` a
Помните нашу функцию pullArg
? Позвольте пересмотреть его немного больше, поскольку вы складываете слева:
pullArg :: (a -> r1 -> r1, a -> r2 -> r2) -> a -> Dual (Endo r1, Endo r2)
pullArg (f, g) = \a -> Dual (Endo $ f a, Endo $ g a)
И это, я утверждаю, является 2-кортежной версией вашего f
или, по крайней мере, изоморфной ей. Вы можете реорганизовать свои функции сгиба в форму a -> Endo ri
, а затем выполните:
let (f'1, ..., f'n) = foldMap (pullArgn f1 ... fn) xs
in (f'1 z1, ..., f'n zn)
Также стоит посмотреть: Композитные потоковые складки, что является дальнейшей разработкой этих идей.