Search code examples
haskellsetterhaskell-lenslenses

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?


Solution

  • 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.