Ответ 1
Вы можете использовать оператор (***)
из Control.Arrow
> map (f *** f) a
или определите свою собственную вспомогательную функцию
> let both f (x, y) = (f x, f y)
> map (both f) a
Я использую этот справедливый бит:
a' = [ (f x, f y) | (x, y) <- a ]
Есть ли лучший способ сделать это?
Вы можете использовать оператор (***)
из Control.Arrow
> map (f *** f) a
или определите свою собственную вспомогательную функцию
> let both f (x, y) = (f x, f y)
> map (both f) a
Альтернативное решение:
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)
Если вы много работаете с парами одинакового типа, может быть полезно переключиться с (,)
на такой тип.
Если вы используете lens
, вы можете использовать over both f
или both %~ f
. Преимущество этого состоит в том, что он более сложный - например, если у вас есть пара списков, вы можете использовать что-то вроде both.mapped +~ toUpper
(:: ([Char],[Char]) -> ([Char],[Char])
).