Есть несколько очень полезных объяснений в этой ветке 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
? 1036 *
Требуются ли даже ограничения 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)