Они выглядят одинаково на сайте приложения, но они разные, конечно. Когда вы применяете одну из этих двух функций, map
или fmap
, к списку значений они будут давать одинаковый результат, но это не значит, что они предназначены для этой же цели.
Запустите сеанс GHCI (Glasgow Haskell Compiler Interactive), чтобы запросить информацию об этих двух функциях, а затем взгляните на их реализации, и вы обнаружите множество различий.
Query 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
, который использует сопоставление образцов, чтобы вытащить головку (x
) с хвоста (xs
) списка, а затем создает новый список, используя конструктор значения :
(cons), чтобы добавить f x
(прочитайте его как "f, примененный к x" ) к рекурсии map
по хвосту, пока список не станет пустым. Стоит заметить, что реализация функции map
не зависит от какой-либо другой функции, а только от самого себя.
Теперь попробуйте запросить информацию о 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)
, а другая возможная реализация - это тот, который задается типом данных 2-кортежа
instance Functor ((,) a) where
fmap f (x,y) = (x, f y)
, а другая возможная реализация - та, которая предоставляется типом списка (конечно!):
instance Functor [] where
fmap f xs = map f xs
, который полагается на функцию map
(обратите внимание на точечную нотацию там ... но это не входит в объем вашего исходного вопроса).
Функция map
может применяться только к списку значений (где значения имеют любой тип), тогда как функция fmap
может применяться гораздо больше типов данных: все те, которые принадлежат классу функтора (например, майбы, кортежи, списки и т. д.). Поскольку тип данных «список значений» также является функтором (поскольку он обеспечивает реализацию для него), тогда fmap
может быть применен к тому же, что и тот же результат, что и map
.
map (+3) [1..5]
fmap (+3) (Just 15)
fmap (+3) (5, 7)