Wyglądają tak samo w witrynie aplikacji, ale są oczywiście inne. Po zastosowaniu jednej z tych dwóch funkcji map
lubfmap
do listy wartości, dadzą one ten sam wynik, ale to nie znaczy, że są przeznaczone do tego samego celu.
Uruchom sesję GHCI (Glasgow Haskell Compiler Interactive), aby uzyskać informacje o tych dwóch funkcjach, a następnie spójrz na ich implementacje, a odkryjesz wiele różnic.
mapa
Zapytaj GHCI o informacje na temat map
Prelude> :info map
map :: (a -> b) -> [a] -> [b]
i zobaczysz, że jest zdefiniowana jako funkcja wysokiego rzędu mająca zastosowanie do listy wartości dowolnego typu, a
dająca listę wartości dowolnego typu b
. Chociaż polimorficzna ( a
iw b
powyższej definicji oznacza dowolny typ), map
funkcja ma być stosowana do listy wartości, która jest tylko jednym możliwym typem danych spośród wielu innych w Haskell. Plikmap
Funkcja nie może być stosowana do czegoś, co nie jest listą wartości.
Jak można przeczytać z kodu źródłowego GHC.Base , map
funkcja jest zaimplementowana w następujący sposób
map _ [] = []
map f (x:xs) = f x : map f xs
który wykorzystuje dopasowanie do wzorca, aby wyciągnąć początek (the x
) z końca (the xs
) listy, a następnie konstruuje nową listę przy użyciu :
konstruktora wartości (minusy), tak aby poprzedzać f x
(czytaj jako "f zastosowane do x" ) do rekurencji map
over the tail, aż lista będzie pusta. Warto zauważyć, że implementacjamap
funkcji nie zależy od żadnej innej funkcji, ale tylko od siebie.
fmap
Teraz spróbuj zapytać o informacje, fmap
a zobaczysz coś zupełnie innego.
Prelude> :info fmap
class Functor (f :: * -> *) where
fmap :: (a -> b) -> f a -> f b
...
Czas fmap
ten definiuje się jako jedną z funkcji, której implementacje muszą być zapewnione przez te typy danych, które chcą należeć do Functor
klasy typów. Oznacza to, że może istnieć więcej niż jeden typ danych, nie tylko typ danych „lista wartości” , który może zapewnić implementację fmap
funkcji. To ma fmap
zastosowanie do znacznie większego zestawu typów danych: rzeczywiście funktorów!
Jak można wyczytać z kodu źródłowego GHC.Base , możliwą implementacją fmap
funkcji jest ta, którą zapewnia Maybe
typ danych:
instance Functor Maybe where
fmap _ Nothing = Nothing
fmap f (Just a) = Just (f a)
a inną możliwą implementacją jest ta zapewniana przez typ danych 2-krotkowych
instance Functor ((,) a) where
fmap f (x,y) = (x, f y)
a inną możliwą implementacją jest ta dostarczona przez typ danych listy (oczywiście!):
instance Functor [] where
fmap f xs = map f xs
który opiera się na map
funkcji.
Wniosek
map
Funkcja może być stosowany do niczego więcej niż listy wartości (gdzie wartości są z każdego rodzaju), natomiast fmap
funkcja może być stosowana znacznie więcej typów danych: wszystkie te, które należą do klasy funktora (np maybes, krotki, listy, etc. ). Ponieważ typ danych „lista wartości” jest również funktorem (ponieważ zapewnia jego implementację), fmap
można go zastosować do programu, dając dokładnie taki sam wynik jak map
.
map (+3) [1..5]
fmap (+3) (Just 15)
fmap (+3) (5, 7)