Подтвердить что ты не робот

В чем разница между `ix` и` at` в библиотеке объективов Haskell

Все, что я знаю, это то, что один работает, а другой нет.

Контекст: У меня есть одна структура данных F, которая содержит Data.Map.Map k S для другой структуры данных S. Моя цель состояла в том, чтобы построить Lens, который дал бы F и k описал бы поле в S.

Трудность состоит в том, что ключ k может отсутствовать на карте. Это прекрасно, что функция может обернуть его возвращение в Maybe. Однако я не мог распространять объектив через "Maybe", используя at. Прочитав много ответов на переполнение стека, я столкнулся с этим.

Оказывается, что замена at на ix решла мои проблемы типа, если я также заменил (^.) на (^?).

Вопрос: Похоже, что at и ix делают то же самое, по крайней мере, в отношении Map. Оба берут ключ и дают "Объектив" для значения на этом ключе. Тем не менее, ix, похоже, хорошо сочетается с оператором композиции функций (.). В чем разница между двумя?


Off Topic Rant:

Мне нравятся операторы infix, как и у следующего парня, но пакет Control.Lens, похоже, немного завышен. Для нового пользователя, имеющего несколько английских имен, и ключ где-то будет понижать кривую обучения. Из-за огромного количества классов-оберток, используемых в библиотеке Lens, особенно сложно вырыть сигнатуры типов, если вы еще не знаете, что происходит. Мой код начинает выглядеть как Perl ради вас.

4b9b3361

Ответ 1

То, что at и ix разные, уже можно заметить, если вы посмотрите на доступные экземпляры для классов, содержащих эти функции:

  • экземпляры at: Map, IntMap, HashMap
  • экземпляры Ixed: [a], Map, ByteString, Text и многое другое

Все экземпляры if at также являются экземплярами ix, но не все экземпляры ix также являются экземплярами at.

Так какая разница между ними? at предназначен для контейнеров, которые позволяют вставлять ключи, которых нет в контейнере. Это, очевидно, возможно для Карты, но не, например. для списка. Чтобы все еще иметь возможность индексировать в список и изменять элементы, которые там есть, ix не позволяет создавать новые элементы, а просто "ничего не делает", когда вы пытаетесь записать ключ, которого там нет.

>>> Data.Map.fromList [('a', 1)] & at 'b' .~ Just 4 
fromList [('a',1),('b',4)] -- Inserts value 
>>> Data.Map.fromList [('a', 1)] & ix 'b' .~ 4  
fromList [('a',1)]          -- Does nothing because key is not present

(Существует также ярлык для a .~ Just b, a ?~ b)

Технически это различие связано с тем, что ix является Traversal, тогда как at является Lens. И поскольку at - это объектив, который "возвращает" "Может быть что-то", вы не можете скомпоновать его с объективом, который занимает просто "Что-то". ix - это обход с 0 или 1 значением, поэтому вы можете составить ix так же, как и любой другой обход (так же, как вы можете написать traverse . traverse). (^?) просто берет первое значение (head) этого обхода.

Вы всегда можете получить ix из at:

ixAt = at . traverse

То же определение уже находится в объективе, за исключением того, что используется (<.) для композиции, чтобы сохранить индекс с. (at и ix являются как проиндексированными объективами/обходами).

Off-topic: большинство операторов из объектива также имеют имена инфикс, вы можете найти (неполную) таблицу по адресу: https://github.com/ekmett/lens/wiki/Operators