На сайте приложения они выглядят одинаково, но, конечно, они разные. Когда вы применяете одну из этих двух функций 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)