2012-11-18 11 views

Antwort

12

Die Functor Instanz ist eigentlich aus dem GHC.Base-Modul, das von Control.Applicative importiert wird.

Wenn ich versuche, die Instanz zu schreiben, die ich will, kann ich sehen, dass es nicht funktioniert, wenn man die Tupel definiert; Die Instanz benötigt nur einen Typparameter, während das 2-Tupel zwei hat.

Eine gültige Functor Instanz würde zumindest auf Tupel sein, (a,a), die den gleichen Typ für jedes Element, aber man kann nichts hinterhältig tun, wie die Instanz definieren auf:

type T2 a = (a,a) 

weil Instanztypen sind nicht erlaubt, Synonyme zu sein.

Das oben beschränkte 2-Tupel Synonym ist logisch die gleiche wie die Art:

data T2 a = T2 a a 

die kann eine Functor Instanz:

instance Functor T2 where 
    fmap f (T2 x y) = T2 (f x) (f y) 

Wie Gabriel in den Kommentaren bemerkt, diese kann für verzweigende Strukturen oder Nebenläufigkeit nützlich sein.

+8

Eigentlich ist das als eine Instanz der Funktorklasse nützlich. Zum Beispiel kann ich einen Baum als 'type Tree a = Free T2 a' definieren. In der Tat beinhalten die meisten Anwendungen dieses Typs (in seiner Eigenschaft als Funktor) eine Verzweigung oder Nebenläufigkeit irgendeiner Art. –

+12

Es ist erwähnenswert, dass Sie 'Control.Lens' verwenden können, wenn Sie angeben möchten, welcher Teil des Tupels zu mappen ist. Ein 'Setter' ist wie eine'Functor'-Instanz, die Sie explizit angeben, also' over_1 (+1) (5,3) '==>' (6,3) '; 'über _2 (* 2) (" witzig, 2) '==>' ("witzig", 4) ';" über beide Längen ("hi", "dort") '==>' (2,5) ';' über (both._1) (* 10) ((1,2), (3,4)) '==>' ((10,2), (30,4)) '. – shachaf

+0

danke @shafaf , das ist ein hilfreicher Kommentar. –

29

Lassen Sie mich diese Antwort mit einer Frage: Welche Ausgabe erwarten Sie, für:

main = print $ fmap (*2) ("funny",2) 

Sie können etwas haben, wie Sie wollen (mit data Pair a = Pair a a oder so), aber als (,) verschiedene Typen haben in Ihr erstes und zweites Argument, Sie haben kein Glück.

7

Paare sind im Wesentlichen wie folgt definiert:

class Functor f where 
    fmap :: (a -> b) -> f a -> f b 

Da die Art der Funktionsargumente und Ergebnisse müssen Art * haben (dh sie repräsentieren:

data (,) a b = (,) a b 

Die Functor Klasse sieht wie folgt aus Werte statt Typ Funktionen, die weitere oder exotischere Dinge angewendet werden können), müssen wir haben a :: *, b :: *, und vor allem für unsere Zwecke, f :: * -> * . Da (,) die Art * -> * -> * hat, muss sie auf einen Typ von Art * angewendet werden, um einen Typ zu erhalten, der geeignet ist, ein Functor zu sein. So

instance Functor ((,) x) where 
    -- fmap :: (a -> b) -> (x,a) -> (x,b) 

So gibt es eigentlich keine Möglichkeit, eine Functor Instanz schreiben etwas anderes zu tun.


Eine nützliche Klasse, die mehr Möglichkeiten bietet, mit Paaren zu arbeiten, ist Bifunctor, von Data.Bifunctor.

class Bifunctor f where 
    bimap :: (a -> b) -> (c -> d) -> f a c -> f b d 
    bimap f g = first f . second g 

    first :: (a -> b) -> f a y -> f b y 
    first f = bimap f id 

    second :: (c -> d) -> f x c -> f x d 
    second g = bimap id g 

Auf diese Weise können Sie Dinge wie die folgenden (aus Data.Bifunctor.Join) schreiben:

newtype Join p a = 
    Join { runJoin :: p a a } 

    instance Bifunctor p => Functor (Join p) where 
    fmap f = Join . bimap f f . runJoin 

Join (,) ist dann im Wesentlichen die gleiche wie Pair, wo

data Pair a = Pair a a 

Natürlich können Sie auch Verwenden Sie einfach die Bifunctor Instanz, um direkt mit Paaren zu arbeiten.

+0

Warum ist '' - fmap :: (a -> b) -> (y, x, a) -> (y, x, b) '' funktioniert nicht für 3-Tupel? –