Sie sehen auf der Anwendungsseite gleich aus, sind aber natürlich unterschiedlich. Wenn Sie eine dieser beiden Funktionen anwenden, map
oderfmap
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 a
ergibt b
. Obwohl polymorph (die a
und b
in der obigen Definition stehen für jeden Typ), soll die map
Funktion auf eine Liste von Werten angewendet werden, die nur ein möglicher Datentyp unter vielen anderen in Haskell ist. Die map
Funktion konnte nicht auf etwas angewendet werden, das keine Werteliste ist.
Wie Sie aus dem GHC.Base- Quellcode lesen können , ist die map
Funktion 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, fmap
und 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 fmap
ist als eine der Funktionen definiert, deren Implementierungen von den Datentypen bereitgestellt werden müssen, die zur Functor
Typklasse 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 fmap
Funktion bereitstellen kann. Das fmap
gilt 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 fmap
Funktion diejenige, die vom Maybe
Datentyp 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 map
Funktion ab.
Fazit
Die map
Funktion kann nur auf eine Liste von Werten angewendet werden (wobei Werte von einem beliebigen Typ sind), während die fmap
Funktion 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), fmap
kann er angewendet werden und liefert auch das gleiche Ergebnis wie map
.
map (+3) [1..5]
fmap (+3) (Just 15)
fmap (+3) (5, 7)