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?
source to share
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)]]
source to share