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?
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óż.
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. :)
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.
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
classFunctor (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:
instanceFunctorMaybewhere
fmap _ Nothing = Nothing
fmap f (Just a) = Just (f a)
a inną możliwą implementacją jest ta zapewniana przez typ danych 2-krotkowych
instanceFunctor ((,) a) where
fmap f (x,y) = (x, f y)
a inną możliwą implementacją jest ta dostarczona przez typ danych listy (oczywiście!):
instanceFunctor [] 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.
fmap
chodzi. Ma na celu zmapowanie funkcji na instancję Functor. Zastanawiam się nad celem tej specjalizacjimap
.Odpowiedzi:
Chciałbym udzielić odpowiedzi, aby zwrócić uwagę na komentarz Augusta :
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óż.
źródło
map and fmap
Już 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 zunifikowanymmap
wypełnieniem.Cytując z
Functor
dokumentacji na https://wiki.haskell.org/Typeclassopedia#Functorźródło
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] -- Defined in ‘GHC.Base’
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 typub
. Chociaż polimorficzna (a
iwb
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óbmap _ [] = [] 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 (thexs
) 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 rekurencjimap
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 ... -- Defined in ‘GHC.Base’
Czas
fmap
ten definiuje się jako jedną z funkcji, której implementacje muszą być zapewnione przez te typy danych, które chcą należeć doFunctor
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 mafmap
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ą zapewniaMaybe
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), natomiastfmap
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 jakmap
.map (+3) [1..5] fmap (+3) (Just 15) fmap (+3) (5, 7)
źródło