2013-07-10 13 views
6

Ich habe diesen Code:Verallgemeinern der "Sequenz" für alle Funktoren?

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

, die im Grunde die Funktion in dem Tupel dem zweiten Element gilt und dann „Extrakte“ die Monade. Da das Tupel ein Funktor ist, gibt es eine Möglichkeit, dies für alle Funktoren zu verallgemeinern? Ich kann eine Implementierung nicht denken, aber die Art Unterschrift sein sollte:

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

es wie der zweite Schritt scheinen würde eine „Sequenz“ Operation wäre, der die Monade Auszüge aus einem anderen Funktors (die Liste). Aber die Reihenfolge ist nicht für alle Funktoren verallgemeinert. Können Sie eine generische Implementierung von fmapM erstellen?

Bearbeiten: Ich habe festgestellt, dass eine alte Version von Umarmungen diese Funktion implementiert hatte. Ich kann den Code jedoch nicht finden. Jetzt wird vorgeschlagen, dass ich faltbar/traversable benutze, um dasselbe zu erreichen.

+0

Ah, ich sehe die 'fmapM' Sie in alten Umarmungen bedeuten, aber das war nur eine weniger allgemeine Version von' Traversable'. Es ist immer noch eine Klasse mit einer anderen Implementierung für jeden Typ. – shachaf

+0

(Sie können 'fmap' - wie auch viele andere Funktionen - von' traverse' ableiten, aber nicht umgekehrt.) – shachaf

Antwort

11

Die Funktion, die Sie suchen ist traverse, von Data.Traversable:

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

Beachten Sie, dass Sie nicht traverse jede Functor - zum Beispiel, (r ->) - so gibt es eine separate Unterklasse von Traversable functors ist. Beachten Sie auch, dass Sie nicht benötigen Monad - nur Applicative (diese Generalisierung ist nützlich).

+0

Eine minimale Implementierung von Traversable für '(,) a' wäre etwas wie' ' sequenzA (a, b) = do {b '<- b; return $ (,) ab '} 'in monadischer Notation und' (,) a <$> b' in applikativer Schreibweise ... aber traversable will '(,) a' auch faltbar sein, und das scheint nicht möglich zu sein ... – BruceBerry

+1

Es ist möglich. Wenn Sie 'traverse' (statt' sequenceA') definiert haben, können Sie sowohl '' Functor'' als auch '' faltbare''-Instanzen automatisch mit 'fmapDefault' und' foldMapDefault' schreiben. – shachaf

+0

'traverse' für' (,) a' sieht so aus: 'traverse f (x, y) = (,) x <$> fy'. 'foldMap' und' fmap' sehen so aus: 'foldMap f (x, y) = fy'; 'fmap f (x, y) = (x, f y)'. 'traverse' ist eine direkte Verallgemeinerung der beiden. – shachaf