András Kovács answer shows how to achieve this with RankNTypes
. If you wish to avoid RankNTypes
, then you can use ALens
and cloneLens
:
f :: a -> ALens' a Int -> (Int, a)
f a l = (newvalue, a & cloneLens l .~ newvalue)
where oldvalue = a^.cloneLens l
newvalue = if oldvalue == 0 then 0 else oldvalue - 1
Control.Lens.Loupe provides operators and functions that work on ALens
instead of Lens
.
Note that in many cases, you should also be able to use <<%~
, which is like %~
but also returns the old value, or <%~
, which returns the new value:
f :: a -> LensLike' ((,) Int) a Int -> (Int, a)
f a l = a & l <%~ g
where g oldvalue = if oldvalue == 0 then 0 else oldvalue - 1
This has the advantage that it can also work with Isos
or sometimes also with Traversals
(when the target type is a Monoid
).