Использование стрелок для свертывания списка кортежей

Иногда вы хотите свернуть список кортежей в один кортеж, используя различные функции сгибания. Например, для склеивания списка результатов runState, получения (в некотором смысле) комбинированного состояния и комбинированного результата.

Рассмотрим следующую реализацию:

wish :: (a -> a' -> a) -> (b -> b' -> b) -> (a,b) -> [(a', b')] -> (a,b)
wish lfn rfn x xs = foldl (\(a,b) -> (lfn a) *** (rfn b)) x xs

Хотя это работает, я чувствую себя некомфортно в этой лямбде. lfn *** rfn сам по себе имеет тип (a,b) -> (a -> a', b -> b'), который я не могу найти способ правильно применить к кортежу, не прибегая к сопоставлению с образцом. Есть ли ясный и элегантный способ, которым я не хватает? Возможно, это может быть библиотечная функция типа (a,a') -> (a -> a, a' -> a') -> (a, a') или совсем другой подход.

Ответы

Ответ 1

Control.Arrow не уделяет много внимания функциям более высокой степени. То, что вам действительно нужно, это функция foo :: (a -> a' -> a'') -> (b -> b' -> b'') -> (a,b) -> (a',b') -> (a'',b''), аналог (***) для функций arity 2. В Data.Biapplicative(из бифунтера пакета) есть такая функция, которая имеет несколько более общую подпись biliftA2 :: Biapplicative w => (a -> b -> c) -> (d -> e -> f) -> w a d -> w b e -> w c f. Поскольку существует биапликативный экземпляр для двухэлементных кортежей, это все, что вам нужно.

Единственная жалоба, которую я вижу против вашего кода, так это то, что кармирование лямбда неочевидно; Я бы предпочел более явный \(a,b) (a',b') -> (lfn a a', rfn b b').

Редактирование заметок: я пришел к выводу, что необходимой функции не существует, и предложил ее определить; вызванный комментариями Карла, я нашел в Biapplicative (более общая подпись типа помешала Hoogle найти его под моей предложенной сигнатурой).