Вони однаково виглядають на сайті програми, але, звичайно, вони різні. Коли ви застосовуєте будь-яку з цих двох функцій, 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)