I'm struggling with using the lens
library for a particular problem. I'm trying to pass
to another function, g
. I pass both the lens and the data structure because g
needs some shared information from the data structure as well as a piece of information. (If it helps, the data structure contains information on a joint probability distribution, but g
only works on either marginal and needs to know which marginal I'm looking at. The only difference between the two marginals is their mean with the rest of their definition being shared in the data structure).
My first attempt looked like this
f :: Functor f => Params -> ((Double -> f Double) -> Params -> f Params) -> a
f p l = g (l %~ upd $ p) l
where upd = ...
g p x = go p p^.x
but that fails during compilation because f
gets inferred as being Identity
for the update and Const Double
for the getter.
What's the best way to accomplish what I want to do? I can imagine being able to do one of the following:
Thanks for any help!
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
).