Why do both itraverse and heraversed functions work in the Haskell lens library?

The Haskell lens library contains a TraversableWithIndex class that defines both itraverse and itraversed functions :

class
    (FunctorWithIndex i t, FoldableWithIndex i t, Traversable t)
    => TraversableWithIndex i t | t -> i where
  itraverse :: Applicative f => (i -> a -> f b) -> t a -> f (t b)
  itraversed :: IndexedTraversal i (t a) (t b) a b

      

IndexedTraversal

expands as follows:

type IndexedTraversal i s t a b =
  forall p f. (Indexable i p, Applicative f) => p a (f b) -> s -> f t

      

itraverse

and itraversed

seem very similar. Why are both needed?


When played with itraverse

and itraversed

, it appears to itraversed

be the only one of the two that can be used with %@~

as AnIndexedSetter

.

Below are the types %@~

, AnIndexedSetter

and Indexed

for completeness:

(%@~) :: AnIndexedSetter i s t a b -> (i -> a -> b) -> s -> t 

type AnIndexedSetter i s t a b =
  Indexed i a (Identity b) -> s -> Identity t 

newtype Indexed i a b = Indexed { runIndexed :: i -> a -> b }

      

Why %@~

does it require AnIndexedSetter

? Why should you use it Indexed

anyway?

Usage Indexed

looks like it will do complex composition as it is not a normal function. What am I missing here?

+3


source to share


1 answer


Indexed optics are not as simple as conventional optics. The usual Traversal

is simple:

type Traversal s t a b = forall f. Applicative f => (a -> f b) -> s -> f t

      

This is exactly the type traverse

with s = c a

and t = c b

, where c

- Traversable

.

It can be assumed that IndexedTraversal i

something similar to (i -> a -> f b)

; but it is not!

type IndexedTraversal i s t a b =
    forall p f. (Indexable i p, Applicative f) => p a (f b) -> s -> f t

      

The restriction Indexable i p

is met (->)

and Indexed

, therefore:

itraverse  :: Applicative f => (i -> a -> f b) -> s -> f t
itraversed :: Applicative f =>      (a -> f b) -> s -> f t  -- (->)
itraversed :: Applicativef  =>   Indexed i a b -> s -> f t

      

Where

newtype Indexed i a b = Indexed { runIndexed :: i -> a -> b } 

      




Why are both needed? itraverse

easier to implement. As a rule, it already exists (for example, traverseWithKey

in containers

).




Operations require specific copies of the optics (eg set

requires ASetter

). Coming soon: easier for the compiler since we don't use Rank2Types

.

Indexed

that is, a separate one is newtype

needed so that we can talk about a -> b

and i -> a -> b

as examples Indexable

; which allows us to decompose indexed optics into regular ones:

Prelude Control.Lens> over itraversed (+1) [1,2,3]
[2,3,4]

      

and having p a (f b) -> q s (f t)

(where p

may be (->)

or Indexable

) create indexed and correct optics:

Prelude Control.Lens> over (itraversed . traversed)  (+1) [[1,2],[3]]
[[2,3],[4]]

      

or

Prelude Control.Lens> iover (itraversed . traversed) (,) [[1,2],[3]]
[[(0,1),(1,2)],[(0,3)]]

      

+4


source







All Articles