Ok, the non-overlapping constraint can't be checked, but it would be nice to be able to say:
over (_1 <*> _3) (+1) (1,1,1) => (2,1,2)
Where <*> becomes something that actually works. :)
I beat my head on this one for an hour or so trying to figure out the right way to implement it to no avail.
I'm going to leave this issue open to remind me that it exists.
I think any solution is going to involve getting creative about tying the knot.
For instance, rwbarton smashed together a version of the single-traversal lazy replace-with-max function:
replaceWithMax t s = result where
(result, Just m) = (`appEndo` Nothing)
<$> runWriter (t (\v -> tell (Endo $ max (Just v)) >> return m) s)
>>>replaceWithMax both (3,4)
The reasoning in whatever combinator is built may have to be similar.
As it turns out, this is impossible. =/
By the time you get done working it out you can't build a 'Traversal', you can only build something like:
type Vertical s t a b = forall m. (Monad m, Applicative m) => (a -> m b) -> s -> m t
This happens because you need to take the entire output from the first Traversal and start a second Traversal on that. If you fmap into the first result, this results in m (m t), for an unknown user-supplied m, which then requires join or (>>=) to forge into the final form.
m (m t)
Even if we allowed for this type it is pretty much useless, since to read from it, you need to use an Accessor, and Accessor provably can't be made into a Monad.
So you can't read and could only barely write to the resulting pseudo-Traversal.
As much as I would love to be able to provide this functionality to the end-user, it is both a theoretical and practical impossibility. =(
You could still use Writer, though, and just discard the unnecessary half of the tuple afterwards.
Writer doesn't actually work, because that requires the value to be ().
(a -> m ()) -> s -> m t
finishes breaking the rest of the laws for you. ;)
Remember you need to be able to let b = a, s = t, for something to be a valid Lens, Traversal, etc.
b = a
s = t
The closest thing to that that can fit the laws is an Action. =/
Why does b have to be restricted to () when you could instead just restrict the whole thing to being Simple and just pass the value through unchanged?
I may have to retract the claim that it is not possible to read from and settle for the claim that the resulting pseudo-traversal doesn't interact with the rest of the ecosystem.
On an unrelated note, a variant of upon that took multiple accessors could be made, perhaps to work.
This would make SimpleTraversal s a a monoid I think, with ignored as identity.
SimpleTraversal s a
While it would form a Monoid, it only forms a monoid if you ignore the Traversal laws. The composed monoids would have to affect disjoint portions of the structure or you'll have human sacrifice, dogs and cats living together -- mass hysteria.