Using the lens twice

I am struggling with using the lens library for a specific problem. I'm trying to convey

  • updated data structure
  • a lens focused on part of this updated structure.

to another function, g . I miss both the lens and the data structure, because g requires some general information from the data structure, as well as some of the information. (If this helps, the data structure contains information on the joint probability distribution, but g works only at the marginal level and needs to find out which marginal I am looking for. The only difference between the two marginals is their average value with the others, their definition is shared in data structure).

My first attempt looked like this:

 f :: Functor f => Params -> ((Double -> f Double) -> Params -> f Params) -> a fpl = g (l %~ upd $ p) l where upd = ... gpx = go pp^.x 

but this is not executed at compile time because f is defined as Identity for update and Const Double for recipient.

What is the best way to accomplish what I want to do? I can imagine that you can do one of the following:

  • make a copy of the lens so that the type of output can be different in each case
  • instead of transmitting the updated structure and lens, I pass the original structure and the lens, which returns the changed value (if I only want to update the part of the structure that the lens is looking at).
  • best design choice for my functions / data structure
  • something completely different

Thanks for any help!

+7
haskell lenses lens
source share
3 answers

András Kovács answer shows how to achieve this with RankNTypes . If you want to avoid RankNTypes , you can use ALens and cloneLens :

 f :: a -> ALens' a Int -> (Int, a) fal = (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 can also use <<%~ , which is similar to %~ , but also returns the old value or <%~ , which returns the new value:

 f :: a -> LensLike' ((,) Int) a Int -> (Int, a) fal = 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 Monoid ).

+9
source share

You want your type signature to look like this:

 f :: Params -> Lens Params Params Double Double -> ... -- alternatively, instead of the long Lens form you can write -- Lens' Params Double 

This is not equivalent to what you wrote in the signature, because the functor parameter is quantified inside Lens :

 type Lens stab = forall f. Functor f => (a -> fb) -> (s -> ft) 

Correct signature means:

 f :: Params -> (forall f. Functor f => (Double -> f Double) -> Params -> f Params) -> ... 

This does not allow the compiler to combine different f parameters for different conditions of using the lens, i.e. E. You can use the lens polymorphically. Please note that you need the RankNTypes or Rank2Types GHC extension so that you can record the signature.

+7
source share

Benno gave the best general answer.

However, there are two other options that I offer here for completeness.

one).

Lens has several Loupe combinators.

http://hackage.haskell.org/package/lens-4.1.2/docs/Control-Lens-Loupe.html

They all have names that include # .

^# and #%= take ALens , which is a lens created with a specific choice of functor.

This can be useful if you need to go through the lists of lenses or you really need several passes.

2.)

Another option and my preferred tactic is to figure out how to do both operations at the same time.

Here you are modifying, but want the value you just set. Well, yes, you can do this using <%~ instead of %~ .

Now you only create an instance of the lens with one choice of functor, and your code becomes faster.

+2
source share

All Articles