Ответ 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