8

In Haskell's lens library, ix and element both take an Int an can be used e.g. to read or write a list element at some index, like this

ghci> [1..10] ^? ix 4
Just 5
ghci> [1..10] & ix 4 .~ 1
[1,2,3,4,1,6,7,8,9,10]

and similarly:

ghci> [1..10] ^? element 4
Just 5
ghci> [1..10] & element 4 .~ 1
[1,2,3,4,1,6,7,8,9,10]

What is the difference between element and ix?

Stephan
  • 746
  • 5
  • 14

3 Answers3

11

With ix you can index not only by number, but by e.g. key in the Maps. The element index in Traverse order.

λ> let m = Map.fromList [("foo", 'f'), ("bar", 'b')]
λ> m ^? ix "foo"
Just 'f'

λ> m ^? element 0 -- Map is ordered container!
Just 'b'

The difference is even more obvious with e.g. IntMap

λ> let im = IntMap.fromList [(1, "one"), (2, "two")]
λ> im ^? ix 1
Just "one"
λ> im ^? element 1
Just "two"
phadej
  • 11,947
  • 41
  • 78
4

element is defined to work with types of the Traversable class, even class members which the lens library doesn't know about. As such it uses only Traversable functions to access the value, which have no concept of any index type specific to the type of value. As such, only Int indices are supported, giving elements in the usual traversal order.

element also gives an IndexedTraversal, giving some extra ways of handling indices.

ix is only defined for types the lens library knows about, but in return it can use different index types dependent on the type of the value.

For lists they give the same result. But the difference can be seen e.g. for Data.Map:

Prelude Control.Lens Data.Map> singleton "a" 3 ^? element "a"

<interactive>:19:28:
    Couldn't match expected type ‘Int’ with actual type ‘[Char]’
    In the first argument of ‘element’, namely ‘"a"’
    In the second argument of ‘(^?)’, namely ‘element "a"’
    In the expression: singleton "a" 3 ^? element "a"

Prelude Control.Lens Data.Map> singleton "a" 3 ^? ix "a"
Just 3
Prelude Control.Lens Data.Map> singleton "a" 3 ^? element 0
Just 3
Prelude Control.Lens Data.Map> singleton "a" 3 ^? ix 0

<interactive>:22:23:
    Could not deduce (Num [Char]) arising from the literal ‘0’
    from the context (Num a)
      bound by the inferred type of it :: Num a => Maybe a
      at <interactive>:22:1-23
    In the first argument of ‘ix’, namely ‘0’
    In the second argument of ‘(^?)’, namely ‘ix 0’
    In the expression: singleton "a" 3 ^? ix 0

As you can see, with element the map is given Int indices, while with ix it is given the key type as index, and trying to switch that around gives a type error.

Ørjan Johansen
  • 18,119
  • 3
  • 43
  • 53
3

element works by walking an entire structure, counting the elements, and traversing the element with the target index. Therefore it always has time complexity O(n) in the size of the structure, and it only works with Int indices.

In contrast, ix has its own class Ixed, and instances rely on the lookup/modify operations of the specific data structures. For example, ix for Data.Sequence is O(log n).

However, ix only works on specific data structures, while elementOf works with any Traversal, Lens or Iso, for example:

[0..10] ^? elementOf (reversed . each . filtered odd) 1
-- Just 7
András Kovács
  • 29,931
  • 3
  • 53
  • 99
  • Thanks. +1 for mentioning the complexity, I was actually worried about that. It makes total sense. So it seems we should use ix where possible. – Stephan Jan 22 '15 at 15:44
  • Why does `element` need to walk the entire structure? – user239558 Apr 21 '20 at 07:54
  • Because the type of `element` is `Traversable t => Int -> IndexedTraversal' Int (t a) a`, and there is no random element access method in `Traversable t`. If we only know this much about a type, the only way of accessing an element is to `traverse` and count elements until the target is reached. – András Kovács Apr 21 '20 at 08:54