Вони однаково виглядають на сайті програми, але, звичайно, вони різні. Коли ви застосовуєте будь-яку з цих двох функцій, mapабо fmap, до списку значень вони дадуть однаковий результат, але це не означає, що вони призначені для тієї ж мети.
Запустіть сеанс GHCI (Glasgow Haskell Compiler Interactive) для запиту інформації про ці дві функції, потім перегляньте їх реалізації, і ви виявите багато відмінностей.
карта
Запит 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
Тепер спробуйте запитати інформацію про, 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)