Можно ли отобразить первый элемент пары без стрелок?


Я смотрю на функторы, прикладные функторы... я не знаю, как попасть туда, куда я хочу, но у меня есть чувство, что следование типам должно приблизить меня.

Существует ли простой способ сделать map-подобным, который применим только к первому элементу 2-кортежа? Взяв first из Control.Arrow и используя Arrow (->), это прекрасно делает трюк:

map . first :: (b -> c) -> [(b, d)] -> [(c, d)]
Меня беспокоит только то, что у меня еще нет настоящей интуиции для стрел, и поэтому я, вероятно, рано или поздно окажусь в глубокой воде, если я продолжай в том же духе. Кроме того, это довольно удобный случай, который не может быть обобщен.

Могу ли я получить ту же функциональность, используя что-то из функторов, монад или чего-то еще, добираясь до сути того, что я хочу? Я играл с

f -> map (f `on` fst)

- вроде бы идеи,но никак не получалось.

5 6

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