На сайте приложения они выглядят одинаково, но, конечно, они разные. Когда вы применяете одну из этих двух функций mapили fmapк списку значений, они дадут одинаковый результат, но это не значит, что они предназначены для одной и той же цели.
Запустите сеанс GHCI (интерактивный компилятор Glasgow Haskell), чтобы запросить информацию об этих двух функциях, затем посмотрите на их реализации, и вы обнаружите много различий.
карта
Запросите GHCI информацию о map
Prelude> :info map
map :: (a -> b) -> [a] -> [b] -- Defined in ‘GHC.Base’
и вы увидите, что она определена как функция высокого порядка, применимая к списку значений любого типа, в aрезультате чего получается список значений любого типа b. Хотя и полиморфна ( aи bв приведенном выше определении обозначают любой тип), mapфункция предназначена для применения к списку значений, который является лишь одним из возможных типов данных среди многих других в Haskell. mapФункция не может быть применена к чему - то, не является списком значений.
Как видно из исходного кода GHC.Base , mapфункция реализована следующим образом
map _ [] = []
map f (x:xs) = f x : map f xs
который использует сопоставление с образцом, чтобы вытащить голову (the x) из хвоста (the xs) списка, а затем создает новый список с помощью :конструктора значения (cons), чтобы добавить f x(прочтите его как «f, примененный к x» ) к рекурсии mapнад хвостом, пока список не станет пустым. Стоит отметить, что реализация mapфункции не зависит от какой-либо другой функции, а только от себя.
fmap
Теперь попробуйте запросить информацию, fmapи вы увидите совсем другое.
Prelude> :info fmap
class Functor (f :: * -> *) where
fmap :: (a -> b) -> f a -> f b
...
-- Defined in ‘GHC.Base’
На этот раз fmapопределяется как одна из функций, реализация которой должна быть обеспечена теми типами данных, которые хотят принадлежать к Functorклассу типов. Это означает, что может быть более одного типа данных, а не только тип данных «список значений» , способных обеспечить реализацию fmapфункции. Это fmapприменимо к гораздо большему набору типов данных: действительно, к функторам!
Как вы можете прочитать из исходного кода GHC.Base , возможная реализация fmapфункции обеспечивается Maybeтипом данных:
instance Functor Maybe where
fmap _ Nothing = Nothing
fmap f (Just a) = Just (f a)
и другая возможная реализация - это тот, который предоставляется типом данных с двумя кортежами
instance Functor ((,) a) where
fmap f (x,y) = (x, f y)
и другая возможная реализация - это тот, который предоставляется типом данных списка (конечно!):
instance Functor [] where
fmap f xs = map f xs
который полагается на mapфункцию.
Вывод
mapФункция может быть применена ни к чему не более чем список значений (где значения являются любого типа) , в то время как fmapфункция может быть применена гораздо больше типов данных: все те , которые принадлежат к классу функтора (например maybes, кортежи, списки и т.д. ). Так как тип данных «список значений» также является функтором (потому что он обеспечивает его реализацию), то fmapего можно применять и к нему, что дает тот же результат, что и map.
map (+3) [1..5]
fmap (+3) (Just 15)
fmap (+3) (5, 7)