Почему индексированные линзы определены так, как они есть?
В этой ветке Reddit есть несколько очень полезных объяснений того, как определяются индексированные линзы, но я хотел бы узнать подробности.
Таким образом, основной момент заключается в том, что индексированные линзы определены так, что их также можно рассматривать как неиндексированные. Эти два класса отвечают за это:
class (Choice p, Corepresentable p,
Comonad (Corep p), Traversable (Corep p),
Strong p, Representable p, Monad (Rep p),
MonadFix (Rep p), Distributive (Rep p),
ArrowLoop p, ArrowApply p, ArrowChoice p
) => Conjoined p where
distrib :: Functor f => p a b -> p (f a) (f b)
conjoined :: (p ~ (->) => q (a -> b) r) -> q (p a b) r -> q (p a b) r
class Conjoined p => Indexable i p where
indexed :: p a b -> i -> a -> b
Вопросы:
0) Почему полезно рассматривать индексированные линзы как неиндексированные? Эдвард (см. Ссылку выше) говорит
Это позволило нам исключить более 30 имен комбинаторов и предоставить общий комбинатор для индексированных и неиндексированных версий большинства операций.
Что это за комбинаторы? Буду признателен за несколько примеров.
Не важно как Conjoined
это умно, я думаю, что это все еще более эффективно (нет необходимости охотиться за полным встраиванием, чтобы словари не передавались) и гибко использовать неиндексированную оптику, когда это возможно. И действительно, я вижу это:
-- If you don't need access to the index, then 'mapped' is more flexible in what it accepts.
imapped :: IndexedSetter i (f a) (f b) a b
(Так как это class Functor f => FunctorWithIndex i f | f -> i
было бы лучше заменить imap (const id) ≡ id
закон с imap (const f) ≡ fmap f
один?)
itraversed . itraversed
поведение, которое игнорирует первый индекс и возвращает последний, выглядит для меня довольно неявным и запутанным. И это усложняет вывод типов и ошибки. Кроме того, я где-то видел, что traversed . traversed
не работает для некоторого определения "работа", это правильно?
Должен ли я писать только индексированную оптику для своей библиотеки и быть в порядке с ней, или все же требуется предоставлять как индексированные, так и неиндексированные варианты комбинаторов?
1) Conjoin
это очень аккуратный трюк, но он далеко не очевиден и недокументирован. Я полагаю, некоторые люди, которые определяют свои собственные индексированные комбинаторы оптики (это часто встречается?), Не знают, что им следует использовать conjoin
, Можно ли навязать использование conjoin
каким-то образом?
Даже MonadFix (Rep p)
а также ArrowLoop p
ограничения, необходимые для Conjoin
работать? Что такое distrib
за? Как это связано с Mapping
?
class (Traversing p, Closed p) => Mapping p where
map' :: Functor f => p a b -> p (f a) (f b)
Что Conjoined ReifiedGetter
экземпляр для?
2) Есть ли менее специальная структура, чем Indexable
? Скажи что-то вроде
class Functor f => StarLike p f | p -> f where
runStarLike :: p a b -> a -> f b
type Indexable i p = (Conjoined p, StarLike p ((->) i)