Let's say I store bank accounts information in an immutable Map
:
val m = Map("Mark" -> 100, "Jonathan" -> 350, "Bob" -> 65)
and I want to withdraw, say, $50 from Mark's account. I can do it as follows:
val m2 = m + ("Mark" -> (m("Mark") - 50))
But this code seems ugly to me. Is there better way to write this?
There's no
adjust
in theMap
API, unfortunately. I've sometimes used a function like the following (modeled on Haskell'sData.Map.adjust
, with a different order of arguments):Now
adjust(m, "Mark")(_ - 50)
does what you want. You could also use the pimp-my-library pattern to get the more naturalm.adjust("Mark")(_ - 50)
syntax, if you really wanted something cleaner.(Note that the short version above throws an exception if
k
isn't in the map, which is different from the Haskell behavior and probably something you'd want to fix in real code.)This could be done with lenses. The very idea of a lens is to be able to zoom in on a particular part of an immutable structure, and be able to 1) retrieve the smaller part from a larger structure, or 2) create a new larger structure with a modified smaller part. In this case, what you desire is #2.
Firstly, a simple implementation of
Lens
, stolen from this answer, stolen from scalaz:Next, a smart constructor to create a lens from "larger structure"
Map[A,B]
to "smaller part"Option[B]
. We indicate which "smaller part" we want to look at by providing a particular key. (Inspired by what I remember from Edward Kmett's presentation on Lenses in Scala):Now your code can be written:
n.b. I actually changed
mod
from the answer I stole it from so that it takes its inputs curried. This helps to avoid extra type annotations. Also notice_.map
, because remember, our lens is fromMap[A,B]
toOption[B]
. This means the map will be unchanged if it does not contain the key"Mark"
. Otherwise, this solution ends up being very similar to theadjust
solution presented by Travis.An SO Answer proposes another alternative, using the
|+|
operator from scalazThe
|+|
operator will sum the values of an existing key, or insert the value under a new key.