Sie sehen auf der Anwendungsseite gleich aus, sind aber natürlich unterschiedlich. Wenn Sie eine dieser beiden Funktionen anwenden, mapoderfmap auf eine Liste von Werten , werden dieselben Ergebnisse erzielt. Dies bedeutet jedoch nicht, dass sie für denselben Zweck bestimmt sind.
Führen Sie eine GHCI-Sitzung (Glasgow Haskell Compiler Interactive) aus, um Informationen zu diesen beiden Funktionen abzufragen. Schauen Sie sich dann deren Implementierungen an und Sie werden viele Unterschiede feststellen.
Karte
Fragen Sie GHCI nach Informationen zu ab map
Prelude> :info map
map :: (a -> b) -> [a] -> [b] -- Defined in ‘GHC.Base’
und Sie werden sehen, dass es als eine Funktion höherer Ordnung definiert ist, die auf eine Liste von Werten eines beliebigen Typs anwendbar ist und eine Liste von Werten eines beliebigen Typs aergibt b. Obwohl polymorph (die aund bin der obigen Definition stehen für jeden Typ), soll die mapFunktion auf eine Liste von Werten angewendet werden, die nur ein möglicher Datentyp unter vielen anderen in Haskell ist. Die mapFunktion konnte nicht auf etwas angewendet werden, das keine Werteliste ist.
Wie Sie aus dem GHC.Base- Quellcode lesen können , ist die mapFunktion wie folgt implementiert
map _ [] = []
map f (x:xs) = f x : map f xs
Dabei wird der Mustervergleich verwendet, um den Kopf (den x) vom Ende (der xs) der Liste abzuziehen. Anschließend wird eine neue Liste erstellt, indem der :(cons) -Wertkonstruktor verwendet wird, um vorangestellt zu werden f x(lesen Sie ihn als "f auf x angewendet" ). zur Rekursion mapüber den Schwanz, bis die Liste leer ist. Es ist erwähnenswert, dass die Umsetzung dermap Funktion nicht von einer anderen Funktion sondern nur von sich selbst.
fmap
Versuchen Sie nun, Informationen zu abfragen, fmapund Sie werden etwas ganz anderes sehen.
Prelude> :info fmap
class Functor (f :: * -> *) where
fmap :: (a -> b) -> f a -> f b
...
-- Defined in ‘GHC.Base’
Diese Zeit fmapist als eine der Funktionen definiert, deren Implementierungen von den Datentypen bereitgestellt werden müssen, die zur FunctorTypklasse gehören möchten . Das bedeutet, dass es mehr als einen Datentyp geben kann, nicht nur den Datentyp "Liste der Werte" , der eine Implementierung für die fmapFunktion bereitstellen kann. Das fmapgilt für einen viel größeren Datensatz: die Funktoren in der Tat!
Wie Sie aus dem GHC.Base- Quellcode lesen können , ist eine mögliche Implementierung der fmapFunktion diejenige, die vom MaybeDatentyp bereitgestellt wird :
instance Functor Maybe where
fmap _ Nothing = Nothing
fmap f (Just a) = Just (f a)
und eine andere mögliche Implementierung ist die, die durch den 2-Tupel-Datentyp bereitgestellt wird
instance Functor ((,) a) where
fmap f (x,y) = (x, f y)
und eine andere mögliche Implementierung ist die, die vom Listendatentyp bereitgestellt wird (natürlich!):
instance Functor [] where
fmap f xs = map f xs
das hängt von der mapFunktion ab.
Fazit
Die mapFunktion kann nur auf eine Liste von Werten angewendet werden (wobei Werte von einem beliebigen Typ sind), während die fmapFunktion auf viel mehr Datentypen angewendet werden kann: alle, die zur Funktorklasse gehören (z. B. Maybes, Tupel, Listen usw.). ). Da der Datentyp "Werteliste" auch ein Funktor ist (weil er eine Implementierung dafür bereitstellt), fmapkann er angewendet werden und liefert auch das gleiche Ergebnis wie map.
map (+3) [1..5]
fmap (+3) (Just 15)
fmap (+3) (5, 7)