Можно ли отобразить первый элемент пары без стрелок?
Я смотрю на функторы, прикладные функторы... я не знаю, как попасть туда, куда я хочу, но у меня есть чувство, что следование типам должно приблизить меня.
Существует ли простой способ сделать map
-подобным, который применим только к первому элементу 2-кортежа? Взяв first
из Control.Arrow
и используя Arrow (->)
, это прекрасно делает трюк:
map . first :: (b -> c) -> [(b, d)] -> [(c, d)]
Меня беспокоит только то, что у меня еще нет настоящей интуиции для стрел, и поэтому я, вероятно, рано или поздно окажусь в глубокой воде, если я продолжай в том же духе. Кроме того, это довольно удобный случай, который не может быть обобщен.
Могу ли я получить ту же функциональность, используя что-то из функторов, монад или чего-то еще, добираясь до сути того, что я хочу? Я играл с
f -> map (f `on` fst)
- вроде бы идеи,но никак не получалось.
5 ответов:
Стрелки имеют хорошие комбинаторы для работы с кортежами. Вы можете почти думать о них как о недостающих функциях кортежа!
Так, например,
> :t \f -> map (f *** id) :: (b -> c) -> [(b, c')] -> [(c, c')]
- это полезный способ отображения по первому компоненту.
Другая абстракция, которая может делать такие вещи, - это бифунктор. Эдвард Kmett есть пакет под названием bifunctors. Данные.Bifunctor имеет класс типа именно для этой функциональности и включает в себя экземпляр для 2-х кортежей.
Итак, вы ищете функцию типа
(a -> b) -> (a,c) -> (b,c)
. Было бы здорово, если бы вы могли просто написатьНо, к сожалению, секции кортежей работают только на уровне значений. Я не знаю, есть ли теоретическая причина для этого; я полагаю, что это портит унификацию типов высшего порядка.{-# LANGUAGE TupleSections #-} instance Functor (,c) where fmap f (x,c) = (f x, c)
Хайу пришел с пакетом Data.Tuple.HT , где функция называется
mapFst
.Без перехода к бифункторам (экземпляр Бифунктора для
(,)
действительно делает то, что вы хотите и ничего больше) или стрелы, нет никакого способа получить то, что вы хотите, что я знаю, по крайней мере.
Я думаю, что проблема в том, что есть слишком много способов - я думаю, что я бы пошел с Советом Даниэля Вагнера - но вот еще один для вашего развлечения:
{-#LANGUAGE DeriveFunctor, MultiParamTypeClasses #-} import Control.Newtype newtype P a b = P {p:: (b, a)} deriving (Show,Eq,Ord,Functor) instance Newtype (P a b) (b,a) where pack = P; unpack = p -- *Main> fmap even ("Hi",4) -- ("Hi",True) -- *Main> map (fmap even) [("Hi",4),("Bye",5)] -- [("Hi",True),("Bye",False)] -- *Main> under P (fmap even) (4,"Hi") -- (True,"Hi") -- *Main> map (under P (fmap even) ) [(4,"Hi"),(5,"Bye")] -- [(True,"Hi"),(False,"Bye")]
Ну, есть пакетБифунктор .
Или вы можете использовать обратный тип пары:
data Flip a b = Flip b a instance Functor (Flip a) where fmap f (Flip x y) = Flip (f x) y