ekmett / lens

Lenses, Folds, and Traversals - Join us on web.libera.chat #haskell-lens
http://lens.github.io/
Other
2.03k stars 271 forks source link

Lifting isos through traversals #887

Open masaeedu opened 4 years ago

masaeedu commented 4 years ago

Hi there. What do you think about adding an operator like:

liftIso :: Traversal s t a b -> Traversal t s b a -> Iso' a b -> Iso' s t
liftIso f b i = iso (over f (view i)) (over b (view $ re i))

This lets us do:

test :: Iso' [Bool] [Any]
test = liftIso each each _Unwrapped

test2 :: Iso' (Bool, String) (Any, String)
test2 = liftIso _1 _1 _Unwrapped

-- etc.

Ideally we'd have some sort of indirection that lets us accept a single traversal and re it, but as far as I can tell the re in the library is specialized to lenses and prisms.

In the profunctor optics demanding Foo and CoFoo constraints simultaneously gives you reversibility of the relevant optic. I don't know how that translates to the Haskell lens approach, so my dumb approach is just asking for two traversals.

phadej commented 4 years ago

This looks like mapping yet for arbitrary Setter (Traversal is too strong), e.g.

Prelude Control.Lens Data.Monoid> :t mapping _Wrapped :: Iso' [Any] [Bool]
mapping _Wrapped :: Iso' [Any] [Bool]
  :: (Profunctor p, Functor f) =>
     p [Bool] (f [Bool]) -> p [Any] (f [Any])
masaeedu commented 4 years ago

So I guess if it's generalized to Setter, mapping can be recovered as:

liftIso :: Setter s t a b -> Setter t s b a -> Iso' a b -> Iso' s t
liftIso f b i = iso (over f (view i)) (over b (view $ re i))

mapping :: Functor f => Iso' a b -> Iso' (f a) (f b)
mapping = liftIso mapped mapped

But this is more specialized than mapping from the library. Is there a way to generalize liftIso further so that plugging in mapped twice recovers the original mapping?