Почему индексированные линзы определены так, как они есть? - PullRequest
0 голосов
/ 06 мая 2018

Есть несколько очень полезных объяснений в этой ветке 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)
...