Open ocharles opened 1 year ago
Hmm, we have traverseOf
already, in Optics.Traversal
. Although it only requires a Traversal
and thus correspondingly the functor must be Applicative
, so it is not a drop in replacement, but it should work in your example?
I suppose we could have traverseOfLens
or something as a synonym for toLensVL
, but I'm not sure it is really worth adding another name.
At the very least we could explain this better in Optics.Lens
, though.
Sorry, I should have said why traverseOf
doesn't work. I'm traversing here with Compose ((,) Tool) m
, and that is only an Applicative
if we have Monoid Tool
, which I don't have. This is because:
instance Monoid a => Applicative ((,) a)
This is why I'm looking for a lens-specific traversal, where I don't need as much as Applicative
.
I see, sorry for not reading your example closely enough to spot that it doesn't support Applicative
.
We currently have (excluding indexed versions)
toIsoVL :: Is k An_Iso => Optic k is s t a b -> IsoVL s t a b
toPrismVL :: Is k A_Prism => Optic k is s t a b -> PrismVL s t a b
toLensVL :: Is k A_Lens => Optic k is s t a b -> LensVL s t a b
traverseOf :: (Is k A_Traversal, Applicative f) => Optic k is s t a b -> (a -> f b) -> s -> f t
atraverseOf :: (Is k An_AffineTraversal, Functor f) => Optic k is s t a b -> (forall r. r -> f r) -> (a -> f b) -> s -> f t
traverseOf_ :: (Is k A_Fold, Applicative f) => Optic' k is s a -> (a -> f r) -> s -> f ()
which feels a bit inconsistent.
So it is plausible to add (some name for)
traverseOf' :: (Is k A_Lens, Functor f) => Optic k is s t a b -> (a -> f b) -> (s -> f t)
and perhaps we might also add
toTraversalVL :: Is k A_Traversal => Optic k is s t a b -> TraversalVL s t a b
toAffineTraversalVL :: Is k An_AffineTraversal => Optic k is s t a b -> AffineTraversalVL s t a b
toFoldVL :: Is k A_Fold => Optic' k is s a -> FoldVL s a
(and indexed versions).
Of course these are somewhat redundant, but the fact they are redundant is a deep and interesting property of the van Laarhoven representation. So I can see that application code might well want "traverseOf
for a lens" without bringing the VL representation into it. And by having names for both we can more easily document the fact that e.g. traverseOf === toTraversalVL
.
To summarise: I'm convinced we should add this, I'm just not sure what to call it. Neither traverseOfLens
nor traverseOf'
feels completely satisfactory, and nor does introducing an inevitable import clash by calling it traverseOf
...
No worries! Naming is indeed hard. My intuitive thoughts are something like traverseOneOf
, but now that I write it, it's sounds like "one of many traversals", so that's no good. traverseLens
doesn't sound so bad though - I don't think you need the Of
once you're talking about lenses.
The naming problem comes already from how to name a class:
class Traversable t => Singleton t where
-- the member really should be verb... compare: traverse, map, ..., ...
singleton :: Functor f => (a -> f b) -> t a -> f (t b)
It only has instances for types isomorphic to (c, )
for some c
. (Thus I call it singleton - traversable with a single element).
Agda calls it Decoration
with traverseF
: https://hackage-search.serokell.io/viewfile/Agda-2.6.2.2/src/full/Agda/Utils/Functor.hs#line-56 and I couldn't (quickly) find anything else on Hackage.
And FWIW, traverseOneOf
is particularly bad name, as there is traverse1
in the wild, which does something else what traverseOneOf
would do, even as optics
doesn't support Traversable1
things.
EDIT: In fact AffineTraversal
has similar function as well, atraverseOf
.
So we have:
traverseOf
atraverseOf
traverse1Of
(in theory)toLensVL
- and this name doesn't fit the pattern atm.
In
lens
, we have:When
traverseOf
is given a lens, we getThe only corresponding function in
optics
istoLensVL
. This has the same type and behavior, but the name is much less informative. I'd like to suggestoptics
gets a combinator that has a better name thantoLensVL
.As an example of how I use this:
Here
V.ix
comes fromData.Vector.Sized
.Slot.uninstallTool :: MonadThrow m => Slot -> m (Tool, Slot)
, so by usingtraverseOf
withCompose
, I essentially get the way to update a "toolbank slot", while also returning aTool
as a result.Using
toLensVL
would make this much less obvious.