2013-07-10 7 views
6

Ho questo codice:Generalizzazione "sequenza" per tutti i funtori?

fmapM :: Monad m => (a -> m b) -> (t, a) -> m (t, b) 
fmapM f (id, e) = do 
    ev <- f e 
    return (id, ev) 

che si applica fondamentalmente la funzione per il 2 ° elemento della tupla e poi "estratti" monade. Dato che la tupla è un funtore, c'è un modo per generalizzare questo per tutti i funtori? Non riesco a pensare a un'implementazione, ma la firma di tipo dovrebbe essere:

fmapM :: (Monad m, Functor f) => (a -> m b) -> f a -> m f b 

sembrerebbe come il secondo passo sarebbe un'operazione di "sequenza", che estrae la monade da un altro funtore (la lista). Ma la sequenza non è generalizzata a tutti i funtori. Puoi venire con un'implementazione generica di fmapM?

Modifica: Mi sono reso conto che una vecchia versione di abbracci ha implementato questa funzione. Tuttavia, non riesco a trovare il codice. Ora, si suggerisce di usare pieghevole/attraversabile per ottenere lo stesso risultato.

+0

Ah, vedo 'fmapM' che intendi nei vecchi Hugs, ma quella era solo una versione meno generale di 'Traversable'. È ancora una classe con un'implementazione diversa per ogni tipo. – shachaf

+0

(Puoi derivare 'fmap' - così come molte altre funzioni - da solo' traverse', ma non viceversa.) – shachaf

risposta

11

La funzione che stai cercando è traverse, da Data.Traversable:

traverse :: (Applicative f, Traversable t) => (a -> f b) -> t a -> f (t b) 

Si noti che non è possibile alcuna traverseFunctor - per esempio, (r ->) - quindi c'è una sottoclasse separata di Traversable funtori. Si noti inoltre che non è necessario Monad - solo Applicative (questa generalizzazione è utile).

+0

Un'implementazione minima di Traversable per '(,) a' sarebbe qualcosa di simile a' sequenceA (a, b) = do {b '<- b; return $ (,) ab '} 'in notazione monadica e' (,) a <$> b' nella notazione applicativa ... ma traversable vuole '(,) a' anche Foldable, e ciò non sembra possibile ... – BruceBerry

+1

È possibile. Infatti, se hai definito 'traverse' (piuttosto che' sequenceA'), puoi scrivere automaticamente sia un'istanza 'Functor' che una' Foldable' usando rispettivamente 'fmapDefault' e' foldMapDefault'. – shachaf

+0

'traverse' per' (,) a' assomiglia a: 'traverse f (x, y) = (,) x <$> f y'. 'foldMap' e' fmap' assomigliano a: 'foldMap f (x, y) = f y'; 'fmap f (x, y) = (x, f y)'. 'traverse' è una generalizzazione diretta dei due. – shachaf