2013-11-04 15 views
21

Exercise 5 of the Haskell Typeclassopedia Section 3.2 bittet um einen Beweis oder Gegenbeispiel auf die AussageWas bedeutet es, zwei Funktoren zu komponieren?

Die Zusammensetzung von zwei Funktoren ist auch ein Functor.

Ich dachte zuerst, dass dies überhaupt sprach die fmap Methoden durch zwei separate Instanzen eines Functor definiert komponieren, aber das ist wirklich nicht sinnvoll, da die Typen, wie ich so weit nicht zusammenpassen würde kann sagen. Für zwei Typen f und f' wären die Typen fmapfmap :: (a -> b) -> f a -> f b und fmap :: (a -> b) -> f' a -> f' b, und das scheint nicht wirklich zusammensetzbar zu sein. Also, was bedeutet es, zwei Functors komponieren?

+2

Haben Sie versucht, fmap mit sich selbst in Ghci zu komponieren? d.h. ': t fmap. fmap' – Squidly

+0

@MrBones Danke für den Tipp! Für diejenigen, die keinen Ghci-Zugriff haben, ist die Ausgabe ':: (Functor f1, Functor f) => (a -> b) -> f (f1 a) -> f (f1 b)' – akbiggs

Antwort

24

A Functor gibt zwei Abbildungen: eine auf den Typebene Abbildungstypen Typen (dies ist die in xinstance Functor x where), und eine auf der Ebene Begriff Abbildungsfunktionen auf Funktionen (das ist die in xfmap = x). Sie denken darüber nach, das Mapping auf Termebene zu erstellen, sollten aber darüber nachdenken, das Mapping auf Typposition zu erstellen. Beispiel gegeben

data Compose f g x = Compose (f (g x)) 

Sie

instance (Functor f, Functor g) => Functor (Compose f g) 

schreiben kann? Wenn nicht, warum nicht?

+1

Dies ist ein ausgezeichnete Antwort, die die Artclassopedia-Übung erklärt, ohne die Lösung zu verraten. – Will

11

Die Zusammensetzung von zwei Funktionen ist, wenn Sie eine Funktion innerhalb einer anderen Funktion setzen, wie

round (sqrt 23) 

Dies ist die Zusammensetzung der beiden Funktionen round und sqrt. In ähnlicher Weise ist die Zusammensetzung von zwei functors, wenn Sie einen Funktor in einem anderen Funktors setzen, wie

Just [3, 5, 6, 2] 

Liste ein Funktor ist, und so ist vielleicht. Sie können ein Gefühl dafür bekommen, warum ihre Zusammensetzung auch ein Funktor ist, wenn Sie versuchen herauszufinden, was fmap mit dem obigen Wert machen soll. Natürlich sollte es den Inhalt des inneren Funktors abbilden!

12

Es hilft wirklich über die kategorische Interpretation zu denken, hier ein Funktor F: C -> D nimmt Objekte (Werte) und morphisms (Funktionen) auf Objekte und Morphismen aus einer Kategorie C auf Objekte und Morphismen in einer Kategorie D.

Für eine zweite Funktors G : D -> E die Zusammensetzung von functors G . F : C -> E nimmt gerade die codomain von Ffmap Transformation die Domäne der Gfmap Transformation. In Haskell wird dies mit einer kleinen Neuauspackung erreicht.

import Data.Functor 

newtype Comp f g a = Comp { unComp :: f (g a) } 

compose :: f (g a) -> Comp f g a 
compose = Comp 

decompose :: Comp f g a -> f (g a) 
decompose = unComp 

instance (Functor f, Functor g) => Functor (Comp f g) where 
    fmap f = compose . fmap (fmap f) . decompose 
+2

Hey! Nichts verraten! = P –

+1

Bezieht sich 'f' gleichzeitig auf' funktor' und 'function'? Wenn die Antwort ja ist, warum gilt dann die Typbeschränkung von "Functor f" nicht für "f" in "fmap f"? – Kamel

12

Was spricht, ist die Zusammensetzung der Typkonstruktoren wie [] und Maybe, nicht die Zusammensetzung von Funktionen wie fmap.So zum Beispiel, gibt es zwei Möglichkeiten der Komposition [] und Maybe:

newtype ListOfMabye a = ListOfMaybe [Maybe a] 
newtype MaybeOfList a = MaybeOfList (Maybe [a]) 

Die Aussage, dass die Zusammensetzung von zwei Functors ist ein Functor bedeutet, dass es eine formelhafte Art und Weise eine Functor Beispiel für diese Art des Schreibens:

instance Functor ListOfMaybe where 
    fmap f (ListOfMaybe x) = ListOfMaybe (fmap (fmap f) x) 

instance Functor MaybeOfList where 
    fmap f (MaybeOfList x) = MaybeOfList (fmap (fmap f) x) 

In der Tat kommt die Haskell-Plattform mit dem Modul Data.Functor.Compose, dass Sie eine Compose Art gibt, die diese „kostenlos“ tut:

import Data.Functor.Compose 

newtype Compose f g a = Compose { getCompose :: f (g a) } 

instance (Functor f, Functor g) => Functor (Compose f g) where 
    fmap f (Compose x) = Compose (fmap (fmap f) x) 

Compose ist besonders nützlich bei der GeneralizedNewtypeDeriving extension:

{-# LANGUAGE GeneralizedNewtypeDeriving #-} 

newtype ListOfMaybe a = ListOfMaybe (Compose [] Maybe a) 
    -- Now we can derive Functor and Applicative instances based on those of Compose 
    deriving (Functor, Applicative) 

anzumerken, dass die Zusammensetzung von zwei Applicative s ist auch eine Applicative. Daher sind, da [] und MaybeApplicative sind, auch Compose [] Maybe und ListOfMaybe. Komponieren Applicative s ist eine wirklich nette Technik, die sich heutzutage immer mehr verbreitet, als eine Alternative zu Monade-Transformatoren für Fälle, in denen Sie nicht die volle Leistung von Monaden benötigen.

+0

Sicherlich die brauchbarste aller Antworten hier. Lass mich dich weiter fragen: Wenn du fmap f schreibst (Compose x) was ist der Typ von x? Ich würde sagen, es ist fg a, aber ich bemühe mich immer noch, die Berechnung zu visualisieren (fmap (fmap f) x). Ich muss mir Gedanken über getCompose machen. NB: Ich verstehe, dass der Buchstabe "f" hier in zwei verschiedenen Rollen verwendet wird. –