Они выглядят одинаково на сайте приложения, но они, конечно, разные.Когда вы применяете любую из этих двух функций, map
или fmap
, к списку значений, они дают одинаковый результат, но это не значит, что они предназначены для одной и той же цели.
Выполнитьсеанс GHCI (интерактивный компилятор Glasgow Haskell) для запроса информации об этих двух функциях, а затем взгляните на их реализации, и вы обнаружите много различий.
map
Запрос GHCI для получения информациипримерно map
Prelude> :info map
map :: (a -> b) -> [a] -> [b] -- Defined in ‘GHC.Base’
, и вы увидите, что она определена как функция высокого порядка, применимая к списку значений любого типа a
, что дает список значений любого типа b
.Хотя полиморфный (a
и b
в вышеприведенном определении означают любой тип), функция map
предназначена для применения к списку значений , который является лишь одним из возможных типов данных среди многихдругие в Хаскеле.Функцию map
нельзя применить к чему-то, что не является списком значений.
Как вы можете прочитать из исходного кода GHC.Base , реализована функция map
следующим образом
map _ [] = []
map f (x:xs) = f x : map f xs
, который использует сопоставление с образцом, чтобы вытащить голову (x
) из хвоста (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)
и другим возможнымРеализация - это та, которая предоставляется типом данных list (конечно!):
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)