Путаница вызвана классами типов Haskell и тем фактом, что функции from-fixed-type являются экземпляром Applicative
(он же монада читателя).Это станет более понятным, если вы напишите это со специализированной версией:
type Reader a b = a -> b
fmapFn :: (a -> b) -> Reader c a -> Reader c b
fmapFn = fmap
-- ≡ liftA
-- ≡ (.)
fmap' :: Applicative f => (a -> b) -> f a -> f b
fmap' = fmapFn (<*>) pure
≡ (<*>) . pure
≡ \φ -> (<*>) (pure φ)
≡ \φ fa -> pure φ <*> fa
И на этом этапе требуется применимый закон
fmap f x = pure f <*> x
, поэтому
fmap' ≡ \φ fa -> fmap φ fa
≡ fmap
1012 * Дух *.Но дело в том, что в определении fmap' = fmap' (<*>) pure
(<*>)
и pure
принадлежат функтору, для которого вы хотите, чтобы это в конечном итоге работало, но fmap'
вы , используя на самом деле всегдапринадлежит функтору функции.Это нормально в Haskell: определение в конце концов является полиморфным, так что если верхний уровень знает, как это сделать для всех функторов, то вы, безусловно, можете также использовать его для функтора функции.(Оставляя в стороне вопрос о нетерминации из-за циклической зависимости ...) Однако, поскольку вы определяете его в виде fmap' = ...
, , ограничение мономорфизма включается: если вы пишете fmap' = fmap' (<*>) pure
без подписи на верхнем уровне, компилятор пытается найти конкретный тип, для которого это должно работать, в частности один конкретный функтор.Но какой бы конкретный тип вы ни выбрали, тогда он будет отличаться от fmapFn
, который вы пытаетесь использовать сами.Таким образом, это определение компилируется только с явной сигнатурой, которая заставляет его быть полиморфным (или, альтернативно, с флагом -XNoMonomorphismRestriction
, который заставляет компилятор выбирать полиморфный тип без явной инструкции) .
РЕДАКТИРОВАТЬ Удивительно, но оказывается, что не ограничение мономорфизма, которое пытается сделать тип менее полиморфным, чем необходимо.Чтобы выяснить, что это такое, давайте попробуем найти более простой пример с той же проблемой.Первая попытка:
fromFloat :: RealFrac a => Float -> a
toFloat :: RealFrac a => a -> Float
fromFloat = realToFrac
toFloat = realToFrac
s = fromFloat . s . toFloat
(я выбрал Float
, потому что это не тип default
, который компилятор может выбрать сам.)
Получается, что это компилируется просто отлично, но вместо большинстваобщий тип
s' :: (RealFrac a, RealFrac b) => a -> b
s' = fromFloat . s' . toFloat
он просто выбирает более простой
s :: Float -> Float
... независимо от того, включено ли ограничение мономорфизма. Почему? Не знаю;Я нашел бы этот интересный вопрос, чтобы задать.