All I know is that one works and the other doesn\'t.
Context:
I have one data structure F
which contains a Data.Map.Map k S
That at
and ix
are different is already noticable if you look at the available instances for the classes containing these functions:
At
: Map, IntMap, HashMapIxed
: [a], Map, ByteString, Text, and a lot moreAll instances if At
are also an instance of Ix
, but not all instances of Ix
are also an instance of At
.
So what's the difference between them? At
is for containers that allow inserting keys that are not present in the container. This is obviously possible for a Map, but not e.g. for a list. To still be able to index into a list and change items that are there, Ix
doesn't allow creating new items, but just "does nothing" when you try to write to a key that is not there.
>>> 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
(There is also a shortcut for a .~ Just b
, a ?~ b
)
Technically, this difference comes from the fact that ix
is a Traversal whereas at
is a Lens. And because at
is a Lens that "returns" a Maybe Something, you cannot compose it with a lens that takes just a plain "Something". ix
is a Traversal with 0 or 1 values, so you can compose ix just like any other traversal (just like you can write traverse . traverse
). (^?)
just takes the first value (head) of that traversal.
You can always derive ix
from at
:
ixAt = at . traverse
The same definition is already in lens, except it's using (<.)
for composition to keep the index from at. (at
and ix
are both indexed lenses / traversals).
Off-topic: Most operators from lens also have infix names, you can find a (incomplete) table at: https://github.com/ekmett/lens/wiki/Operators