2012-05-30 10 views
5

Sto osservando i funtori, i funtori applicativi ... Non so come arrivare dove voglio, ma ho la sensazione che seguire i tipi dovrebbe avvicinarmi.Posso mappare il primo elemento di una coppia senza frecce?

C'è un modo semplice per rendere un valore map valido solo per il primo elemento di una tupla da 2? Prendendo first da Control.Arrow e l'utilizzo di Arrow (->), questo fa il trucco bene:

map . first :: (b -> c) -> [(b, d)] -> [(c, d)] 

La mia unica preoccupazione è che devo ancora acquisire una vera e propria intuizione per le frecce, e così io probabilmente mi trovo in acque profonde, prima o più tardi se continuo così. Inoltre, questo sembra essere un caso piuttosto conveniente che non può essere generalizzato.

È possibile ottenere la stessa funzionalità utilizzando qualcosa dai funtori, dalle monadi o da qualsiasi altra cosa, mentre si arriva al cuore di ciò che voglio? Stavo scherzando con

\f -> map (f `on` fst) 

-come idee, ma non riuscivo a arrivarci.

+11

Se si utilizza un'astrazione non si capisce ti mette a disagio, non c'è assolutamente nulla di sbagliato nell'usare la '' mappa 'meno astratta (\ (a, b) -> (fa, b)) '. –

+0

Grazie per tutti i suggerimenti!Questo è stato davvero illuminante. Ho accettato la risposta più votata da te. – Ashe

+0

Controlla http://stackoverflow.com/questions/413930 se ti senti a disagio con 'map. FIRST'. – sdcvvc

risposta

9

Le frecce dispongono di un buon combinatore per operare su tuple. Puoi quasi pensare a loro come le funzioni mancanti della tupla!

Così ad es.

> :t \f -> map (f *** id) 
    :: (b -> c) -> [(b, c')] -> [(c, c')] 

è un modo utile per la mappatura sul primo componente.

+2

Puoi anche usare (da Control.Arrow) "map (first f)" per applicare una funzione al primo elemento di una coppia e allo stesso modo "map (second f)" per applicarlo al secondo elemento. – ozataman

5

Un'altra astrazione che può fare questo genere di cose è un bifunctore. Edward Kmett ha un pacchetto chiamato bifunctors. Data.Bifunctor ha una classe di tipo per esattamente questa funzionalità e include in istanza per 2-tuple.

1

Bene, c'è il pacchetto BiFunctor.

Oppure si potrebbe usare un tipo di accoppiamento invertito:

data Flip a b = Flip b a 

instance Functor (Flip a) where 
    fmap f (Flip x y) = Flip (f x) y 
2

Quindi siete alla ricerca di una funzione di tipo (a -> b) -> (a,c) -> (b,c). Sarebbe bello se potessi scrivere solo

{-# LANGUAGE TupleSections #-} 
instance Functor (,c) where 
    fmap f (x,c) = (f x, c) 

ma purtroppo le sezioni di tupla funzionano solo sul livello di valore. Non so se c'è una ragione teorica per questo; Suppongo che incasini l'unificazione di tipo superiore.

Hayoo ha trovato il pacchetto Data.Tuple.HT in cui la funzione è denominata mapFst.

Senza passare ai bifuntori (l'istanza BiFunctor per (,) fa davvero quello che vuoi e niente più) o le frecce, non c'è modo di ottenere ciò che vuoi, di cui sono a conoscenza almeno.

2

Credo che il problema è che ci sono troppi modi - Credo che vorrei andare con il consiglio di Daniel Wagner - ma ecco un altro per il vostro divertimento:

{-#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")] 
Problemi correlati