Почему индексированные линзы определены так, как они есть?

В этой ветке 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)

0 ответов

Другие вопросы по тегам