daj
daj

Reputation: 7183

What is the syntax for composing setter lenses?

I'm new to lens and I want to compose two "setter" operations to be equivalent to this state0 to new_state2 transformation:

  let new_state1 = field1 %~ (const newVal1) $ state0
  let new_state2 = field2 %~ (const newVal2) $ new_state1

What's the syntax for doing this?

Upvotes: 3

Views: 365

Answers (1)

Bartek Banachewicz
Bartek Banachewicz

Reputation: 39390

Funny enough, lenses compose just like functions: with a (.):

setterAB :: Lens' A B
setterBC :: Lens' B C

setterAC = setterAB . setterBC

In your example, though, you don't want to compose lenses; you want to compose transformations (which is both the lens and the actual operation), and there are two ways to do that.

Oh, and before we actually get to that, let's simplify your code a bit, using (.~) ("set") instead of (%~) ("modify"):

let new_state1 = field1 .~ newVal1 $ state0
let new_state2 = field2 .~ newVal2 $ new_state1

Direct, via &

There's a fancy & operator that works pretty well. It's just flip ($):

let new_state1 = state0 & field1 .~ newVal1
let new_state2 = new_state & field2 .~ newVal2

Which means you can now write:

let new_state = 
    state0 
        & field1 .~ newVal1
        & field2 .~ newVal2

Monadic

Even better, if you're actually having a State somewhere, you can get rid of that passing completely and put that in a monad:

let new_state = (flip execState state0) $ do
    field1 .= newVal1
    field2 .= newVal2

They are defined in terms of MonadState, so if you're in a monad stack, you can use that instance directly, or use StateT in order to get more effects available for the setters.

Upvotes: 6

Related Questions