Jaki jest cel mapy w Haskell, kiedy jest fmap?

99

Wszędzie, gdzie próbowałem używać map, fmaprównież działało. Dlaczego twórcy Haskella odczuwali potrzebę mapfunkcji? Czy nie mogło to być po prostu tym, co jest obecnie znane jako fmapifmap które można usunąć z języka?

Clark Gaebel
źródło
13
Myślę, że pytasz „Jaki jest sens fmap w Haskell”?
zw324
12
Wiem o co fmapchodzi. Ma na celu zmapowanie funkcji na instancję Functor. Zastanawiam się nad celem tej specjalizacji map.
Clark Gaebel

Odpowiedzi:

95

Chciałbym udzielić odpowiedzi, aby zwrócić uwagę na komentarz Augusta :

Tak się nie dzieje. Zdarzyło się, że typ mapy został uogólniony, aby objąć Functor w Haskell 1.3. To znaczy, w Haskell 1.3 fmap nazywał się map. Ta zmiana została następnie cofnięta w Haskell 1.4 i wprowadzono fmap. Powód tej zmiany był pedagogiczny; podczas nauczania Haskella początkującym bardzo ogólny typ mapy utrudniał zrozumienie komunikatów o błędach. Moim zdaniem nie był to właściwy sposób rozwiązania problemu.

Haskell 98 jest postrzegany jako krok wstecz przez niektórych Haskellerów (w tym mnie), poprzednie wersje definiowały bardziej abstrakcyjną i spójną bibliotekę. No cóż.

luqui
źródło
16
Czy te kroki wstecz są gromadzone i dokumentowane w dowolnym miejscu? Ciekawie byłoby zobaczyć, co jeszcze uznano za krok wstecz i czy są też dla nich lepsze rozwiązania.
Davorak
3
map and fmapJuż od dłuższego czasu - został ponownie zagotować na Haskell-prime listy w sierpniu 2006 roku - haskell.org/pipermail/haskell-prime/2006-August/thread.html . Jako kontrapunkt wolę status quo. Wydaje mi się cenne, że istnieje podzbiór Haskell, który z grubsza odpowiada Mirandzie. W Wielkiej Brytanii Miranda była używana jako język do nauczania matematyki, nie tylko informatyki. Jeśli ta nisza nie jest już stracona przez niefunkcjonalny język (np. Mathematica), nie widzę Haskella ze zunifikowanym mapwypełnieniem.
Stephen Tetley
35
Chciałbym ponadto zauważyć, dla każdego, kto jeszcze nie był świadomy, że augustss to Lennart Augustsson, który ze wszystkich praktycznych względów był częścią społeczności Haskellów, zanim istniał Haskell, por. Historia Haskella , więc komentarz, o którym mowa, nie jest w żaden sposób plotką z drugiej ręki!
CA McCann
3
Na wiki Haskell istnieje teraz strona Nitpicks, na której wspomniano o tym problemie.
Alexey
1
To zabawne, że taka decyzja została podjęta ze względów pedagogicznych, ponieważ cały czas myliłem fmap z flatmapą, ucząc się Haskella. Powinni byli stworzyć razem grupę fokusową n00b. :)
Danny Andrews
28

Cytując z Functordokumentacji na https://wiki.haskell.org/Typeclassopedia#Functor

Możesz zapytać, dlaczego potrzebujemy oddzielnej mapfunkcji. Dlaczego po prostu nie zrezygnować z bieżącej mapfunkcji tylko do listy i zmienić fmapjej nazwę na map ? Cóż, to dobre pytanie. Zwykłym argumentem jest to, że ktoś, kto dopiero uczy się języka Haskell, gdy używa go mapniepoprawnie, wolałby raczej zobaczyć błąd dotyczący list niż about Functor.

Andrei Bozantan
źródło
1
To ma dla mnie sens.
hbobenicio
25

Wyglądają tak samo w witrynie aplikacji, ale są oczywiście inne. Po zastosowaniu jednej z tych dwóch funkcji maplubfmap 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]   -- Defined in ‘GHC.Base’

i zobaczysz, że jest zdefiniowana jako funkcja wysokiego rzędu mająca zastosowanie do listy wartości dowolnego typu, adająca listę wartości dowolnego typu b. Chociaż polimorficzna ( aiw bpowyższej definicji oznacza dowolny typ), mapfunkcja ma być stosowana do listy wartości, która jest tylko jednym możliwym typem danych spośród wielu innych w Haskell. PlikmapFunkcja nie może być stosowana do czegoś, co nie jest listą wartości.

Jak można przeczytać z kodu źródłowego GHC.Base , mapfunkcja 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 mapover 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, fmapa zobaczysz coś zupełnie innego.

Prelude> :info fmap
class Functor (f :: * -> *) where
  fmap :: (a -> b) -> f a -> f b
  ...
  -- Defined in ‘GHC.Base’

Czas fmapten definiuje się jako jedną z funkcji, której implementacje muszą być zapewnione przez te typy danych, które chcą należeć do Functorklasy 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ę fmapfunkcji. To ma fmapzastosowanie 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ą fmapfunkcji jest ta, którą zapewnia Maybetyp 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

mapFunkcja może być stosowany do niczego więcej niż listy wartości (gdzie wartości są z każdego rodzaju), natomiast fmapfunkcja 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ę), fmapmoż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)
Paolo Angioletti
źródło