Control.Lens.Tutorial говорит:
type Traversal' a b = forall f . Applicative f => (b -> f b) -> (a -> f a) type Lens' a b = forall f . Functor f => (b -> f b) -> (a -> f a)Обратите внимание, что единственная разница между Lens и Traversal — это ограничение класса типа. Объект Lens имеет ограничение Functor, а Traversal имеет аппликативное ограничение. Это означает, что любая линза автоматически также является допустимым обходом (поскольку Functor является суперклассом Applicative).
Я просто не соблюдаю здесь логическую последовательность. Мы знаем, что каждый Applicative — это Functor. Не следует ли из этого вообще, что каждый Traversal' является Lens'? Однако руководство приходит к обратному выводу.





Lens' работает для всех функторов, включая все аппликативные функторы, поэтому, если функция g является Lens', то g также является Traversal'.
Traversal' работает для всех аппликативных функторов, но не обязательно для всех функторов. Таким образом, если функция h является Traversal', это не обязательно Lens'.
(Я не тот, кто описывает это формально, но, по крайней мере, мне кажется, что типы «контравариантны» в своих ограничениях. Lens' является подтипом Traversal' именно потому, что Functor является суперклассом Applicative.)
Другой способ выразить это, который может помочь: если бы линза была функтором, а обход был аппликативом, логика в вопросе имела бы смысл. Скорее, линза — это нечто, что принимает в качестве аргумента функтор, а обход — аппликатив. Таким образом, более общий пункт — это тот, который имеет более общий аргумент.
Честно говоря, мне бы очень хотелось, чтобы кто-нибудь (точно) описал, как работает forall f. Я предполагаю, что хороший ответ объяснил бы разницу между Applicative f => ... и forall f . Applicative f => .... (Я прав, полагая, что это необходимый forall, а не просто явный forall, верно?)
(Либо в новом ответе, либо в редактировании этого; в последнем случае я согласен изменить это на вики сообщества.)
Я не уверен, откуда вы взяли имена f и g (вероятно, просто использовали имена общих функций?), но не сразу понятно, что вы не ссылаетесь на переменную типа f из определений в вопросе.
@chepner Возможно, вам понравится мой ответ в Почему число может действовать как дробное?, который пытается точно описать, как forall работает и как он взаимодействует с классами типов.
Обычно я не сторонник милых аналогий с «существами из реального мира», но этот вопрос, вероятно, станет яснее, если рассмотреть следующий пример:
class Animal a where feed :: a -> ArmouredGlove -> IO String
instance Animal Cat where feed _ _ = print "Miaow"
instance Animal Dog where feed _ _ = print "Woof"
instance Animal Lion where feed _ _ = print "Rwaaar"
class Animal a => Pet a where cuddle :: a -> UnprotectedHand -> IO String
instance Pet Cat where cuddle _ _ = print "Purr"
instance Pet Dog where cuddle _ _ = print "Yawn"
type PetOwner = ∀ a . Pet a => a -> IO String
type ZooWarden = ∀ a . Animal a => a -> IO String
peter :: PetOwner
peter = \pet -> cuddle pet peter'sHand
zoey :: ZooWarden
zoey = \animal -> feed animal zoey'sGlove
Здесь ZooWarden — это подтип PetOwner. Зои прекрасно справляется с домашним животным, она может кормить любую собаку, а также львов. Но у Питера нет такой квалификации, он может обращаться только с безобидными домашними животными. Таким образом, именно потому, что он ограничен более конкретным классом животных, он сам принадлежит к более общему типу личности, чем специализированный смотритель зоопарка.
Почему здесь присутствуют типы Peter'sHand и Zoey'sGlove? Разве они не должны быть конкретными примерами UnprotectedHand и ArmouredGlove?
@А.Р. они конструкторы. Но да, возможно, это не имеет особого смысла, я придаю им простые значения.
Рассмотрим представление классов типов с передачей словаря. Вот как на самом деле реализуются классы типов в GHC.
В этой форме классы типов, такие как Functor и Applicative, выглядят примерно так:
data Functor f =
MkFunctor
{ fmap :: (a -> b) -> f a -> f b }
data Applicative f =
MkApplicative
{ appFunctor :: Functor f
, pure :: a -> f a
, (<*>) :: f (a -> b) -> f a -> f b
}
Экземпляры — это значения этих типов, например так:
listFunctor :: Functor []
listFunctor =
MkFunctor
{ fmap = map }
Теперь предположим, что у нас есть линза someLens :: Lens' A B и мы хотим превратить ее в обход. Давайте расширим синоним типа и воспользуемся передачей словаря:
someLens :: Functor f -> (b -> f b) -> (a -> f a)
В этом стиле вы можете использовать его с функтором списка вот так someLens listFunctor.
Нам нужен обход someTraversal :: Traversal' A B. Когда мы реализуем это, нам нужно будет предоставить someLens значение Functor f. Но у нас уже есть значение Applicative f:
someTraversal :: Applicative f -> (b -> f b) -> (a -> f a)
someTraversal applicativeDict = someLens (appFunctor applicativeDict)
Мы можем просто использовать метод доступа к полю, который получает реализацию Functor для предоставленного нам экземпляра Applicative, и у нас есть Traversal. Поле доступа имеет тип appFunctor :: Applicative f -> Functor f.
Вам также может понравиться Почему Num может действовать как дробный?.