7

I find myself using this pattern often:

do
    let oldHeaders = mail ^. headers
    put $ (headers .~ (insert header value oldHeaders)) mail

which seems like the kind of thing Control.Lens should be able to do, but I guess I just haven't found the right operator yet. Is there a better way? Also, is there anything else I should be doing differently in this code?

2 Answers 2

11

You can use a chain of Lenses and Traversals to access the inner header value directly and update it.

put $ mail & headers . at header ?~ value

Note that (?~) is just shorthand for \lens value -> lens .~ Just value. The Just is needed to indicate to the at lens that we want to insert a value if it doesn't exist already.

If mail in the first line comes from the state monad like this

do
  mail <- get
  let oldHeaders = mail ^. headers
  put $ (headers .~ (insert header value oldHeaders)) mail

then it's simpler to write that with modify :: MonadState s m => (s -> s) -> m ()

modify (headers . at header ?~ value)

Which, as suggested by Ørjan Johansen in the comments, can be written most pithily as

headers . at header ?= value
Sign up to request clarification or add additional context in comments.

6 Comments

Exactly what I was looking for! Now I just need to step through it so I make sure I understand it.
(Tried to make this an edit but was rejected:) Remembering that many of the pure setter operators ending in ~ have monadic state updating versions ending in =, we can combine the ?~ and the modify: headers . at header ?= value
Couldn't you just use headers . at header . traversed .= value?
No -- at header . traversed .= value and at header ?= value are different. The former (which can also be written as ix header .= value) can only change an existing value, and will do nothing if the value doesn't exist.
@ØrjanJohansen I added your suggestion with a note.
|
7

You don't usually need to get and put explicitly in the State monad when using lenses. In your case, you can use the operator ?= to update the state directly:

example = do
  headers . at header ?= value

You can also modify any lens with a function using %=:

example2 = do
  headers %= insert header value

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.