В Haskell, почему карта является отдельной функцией для fmap? [Дубликат]

Другой общий случай, когда можно получить это исключение, включает в себя насмешливые классы во время модульного тестирования. Независимо от используемой насмешливой структуры, вы должны убедиться, что все соответствующие уровни иерархии классов должным образом высмеиваются. В частности, все свойства HttpContext, на которые ссылается тестируемый код, должны быть изделены.

См. « Исключение NullReferenceException при проверке пользовательского AuthorizationAttribute » для несколько подробного примера.

80
задан Prateek 3 December 2011 в 09:53
поделиться

3 ответа

Я хотел бы сделать ответ, чтобы обратить внимание на комментарий augustss :

На самом деле это не так. Случилось так, что тип карты был обобщен, чтобы охватить Functor в Haskell 1.3. I.e., в Haskell 1.3 fmap называлась картой. Затем это изменение было возвращено в Haskell 1.4 и введен fmap. Причина этого изменения была педагогической; при обучении Haskell новичкам очень общий тип карты затруднял понимание сообщений об ошибках. На мой взгляд, это был неправильный способ решить проблему.

Haskell 98 рассматривается как шаг назад от некоторых Haskellers (включая меня), предыдущие версии, определяющие более абстрактные и согласованная библиотека. О, хорошо.

75
ответ дан Matthias Braun 31 August 2018 в 18:31
поделиться

Цитата из документации Functor в https://wiki.haskell.org/Typeclassopedia#Functor

Вы можете спросить, почему нам нужен отдельный map. Почему бы просто не покончить с текущей функцией map только для списка и вместо этого переименовать fmap в map? Ну, это хороший вопрос. Обычный аргумент состоит в том, что кто-то, просто изучая Haskell при неправильном использовании map, скорее увидит ошибку в списках, чем о Functor.

18
ответ дан Andrei Bozantan 31 August 2018 в 18:31
поделиться

Они выглядят одинаково на сайте приложения, но они разные, конечно. Когда вы применяете одну из этих двух функций, map или fmap, к списку значений они будут давать одинаковый результат, но это не значит, что они предназначены для этой же цели.

Запустите сеанс GHCI (Glasgow Haskell Compiler Interactive), чтобы запросить информацию об этих двух функциях, а затем взгляните на их реализации, и вы обнаружите множество различий.

map

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

Теперь попробуйте запросить информацию о 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)
9
ответ дан Paolo Angioletti 31 August 2018 в 18:31
поделиться
Другие вопросы по тегам:

Похожие вопросы: