Есть ли лучший способ применить функцию к обоим элементам пары в списке, чем понимание списка?

Я использую этот справедливый бит:

a' = [ (f x, f y) | (x, y) <- a ]

Есть ли лучший способ сделать это?

Ответ 1

Вы можете использовать оператор (***) из Control.Arrow

> map (f *** f) a

или определите свою собственную вспомогательную функцию

> let both f (x, y) = (f x, f y)
> map (both f) a

Ответ 2

Альтернативное решение:

import Data.Bifunctor

bimap f f pair

Bifunctor.bimap в основном совпадает с Arrow.(***), но работает и для других бифункторов (например, Either a b).

Отступление:

Причина, по которой в вашем случае нет ничего предопределенного, заключается в том, что вы не можете писать экземпляры Functor, Applicative и т.д. для (,), имеющие один и тот же тип элемента дважды. С собственным "векторно-подобным" типом у вас не было бы этой проблемы:

data Pair a = Pair a a deriving Show

instance Functor Pair where
  fmap f (Pair x y) = Pair (f x) (f y)

Теперь вы можете писать такие вещи, как map (fmap (+1)) [Pair 12 14, Pair 17 18]. Или, если вы хотите использовать разные операции на вашем Pair, вы можете пойти еще на один шаг:

instance Applicative Pair where 
  pure x = Pair x x
  (Pair f g) <*> (Pair x y) = Pair (f x) (g y)

Если вы много работаете с парами одинакового типа, может быть полезно переключиться с (,) на такой тип.

Ответ 3

Если вы используете lens, вы можете использовать over both f или both %~ f. Преимущество этого состоит в том, что он более сложный - например, если у вас есть пара списков, вы можете использовать что-то вроде both.mapped +~ toUpper (:: ([Char],[Char]) -> ([Char],[Char])).