The type of van Laarhoven lenses forall f. Functor f => (i -> f i) -> o -> f o seems to be made for effectful updates i -> f i. At least so I believed. And I had to pay dearly because of that.
Even if you didn’t use any lenses, modifyM on its own would already be incorrect though, wouldn’t it?
You’re reading the state, updating it in an effectful way that can include state modifications and then overriding those state modifications by writing the result.
Then why use lenses in the first place?? You’re using a lens to focus on a specific subpart, but then you’re modifying the another subpart too. It kind of feels like a mapM except you’re allowed to modify the list in the callback. It’s a red flag; you might be using the wrong abstraction.
When I first saw your blog post, I assumed modifyM would be used for stuff like “modify a field based on the contents of a file”, which makes a bit more sense for a lens operation. You could still shoot yourself in the foot, but at least it has some usefulness.
Of all the mysterious things about this, this one seems least mysterious. The point is the the i -> m i function gives you the new value of i. If m has also modified in the meantime I wouldn’t be surprised if that modification was superseded by the returned i.