Який сенс на карті в Хаскелі, коли є fmap?


97

Скрізь, де я намагався використовувати map, fmapпрацював також. Чому творці Haskell відчули потребу у mapфункції? Хіба це не може бути просто те, що наразі відомо як fmapі яке fmapможна видалити з мови?


11
Я думаю, ви запитуєте: "Який сенс fmap у Haskell"?
zw324,

11
Я знаю, в чому сенс fmap. Це відображення функції над екземпляром Functor. Мені цікаво про мету спеціалізації map.
Clark Gaebel

Відповіді:


95

Я хотів би дати відповідь, щоб звернути увагу на коментар серпні :

Насправді це не так. Відбулося те, що тип карти був узагальнений, щоб охопити Functor в Haskell 1.3. Тобто в Haskell 1.3 fmap називався map. Потім цю зміну було скасовано в Haskell 1.4 та введено fmap. Причиною цієї зміни була педагогічна; навчаючи Haskell для початківців, дуже загальний тип карти ускладнював розуміння повідомлень про помилки. На мій погляд, це був не правильний спосіб вирішення проблеми.

Деякі Haskeller (включаючи мене) Haskell 98 розглядають як крок назад, попередні версії визначали більш абстрактну та послідовну бібліотеку. Що ж, добре.


16
Чи збираються та десь документуються ці кроки назад? Було б цікаво подивитися, що ще вважалося відступним кроком, і чи є для них кращі рішення.
Davorak

3
Файл map and fmapіснує довгий час - він був повторно підігрітий у списку розсилки Haskell-prime у серпні 2006 року - haskell.org/pipermail/haskell-prime/2006-August/thread.html . Як контрапункт я віддаю перевагу статус-кво. Мені здається цінним, що існує підмножина Хаскелла, яка приблизно відповідає Міранді. У Великобританії Міранду використовували як мову навчання для студентів математики, а не лише для студентів інформатики. Якщо ця ніша ще не загублена для нефункціональної мови (наприклад, Mathematica), я не бачу Haskell з уніфікованим mapзаповненням.
Stephen tetley

35
І я хотів би також зауважити, для тих, хто ще не знав, що серпнем є Леннарт Авгусссон, який з усіх практичних цілей є частиною спільноти Хаскелла ще до існування Хаскелла, пор. Історія Хаскелла , тож коментар, про який йдеться, жодним чином не є чутками б / у!
CA McCann

3
Зараз на вікі Haskell є сторінка Nitpicks, де згадується ця проблема.
Олексій

Забавно, що це рішення було прийнято з педагогічних міркувань, тому що я весь час плутав fmap з flatmap, коли вивчав Haskell. Вони повинні були створити фокус-групу n00b. :)
Danny Andrews

27

Посилання з Functorдокументації на https://wiki.haskell.org/Typeclassopedia#Functor

Ви можете запитати, навіщо нам потрібна окрема mapфункція. Чому б просто не покінчити з поточною mapфункцією лише для списку та не перейменувати fmapна map замість цього? Ну, це хороше питання. Звичайний аргумент полягає в тому, що той, хто тільки вивчає Haskell, при mapнеправильному використанні , набагато швидше побачить помилку щодо списків, ніж про Functor.


1
Для мене це має великий сенс.
hbobenicio

25

Вони однаково виглядають на сайті програми, але, звичайно, вони різні. Коли ви застосовуєте будь-яку з цих двох функцій, 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)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.