I'm trying to learn scalaz7 lenses. Is there a better way to chain set operations?
case class Outer(left: Inner, right: Inner)
case class Inner(top: Int, bottom: Int)
val left = Lens.lensu[Outer, Inner](
(o,v) => o.copy(left = v),
_.left
)
val right = Lens.lensu[Outer, Inner](
(o,v) => o.copy(right = v),
_.right
)
val top = Lens.lensu[Inner, Int](
(o,v) => o.copy(top = v),
_.top
)
val leftTop = left >=> top
val rightTop = right >=> top
val outer0 = Outer(Inner(10,20), Inner(30, 40))
val outer1 = rightTop.set(leftTop.set(outer0, 11), 33)
Update:
I have a feeling that the answer might be to use the state monad, though I barely understand why this seems to work. Would be interested to know if there is a neater way.
val modifier = for{
_ <- leftTop := 11
_ <- rightTop := 33
} yield Unit
modifier(outer0)._1 // = Outer(Inner(11,20),Inner(33,40))
You can simplify the State monad version somewhat:
Or, if you prefer:
Your original State version looks a little weird since the
<-
in afor
statement is just syntax sugar for calls to.flatMap
. Simplifying things a bit, the result ofleftTop := 11
has a type likeState[Outer, Outer, Int]
, which is roughly equivalent to a function with typeOuter => (Outer, Int)
. State keeps track of theOuter
part of the result and passes theInt
part to.flatMap
. Since you don't care about theInt
result, you assign it to_
and ignore it.The
>>
does the same thing, it's a.flatMap
that ignores it's argument and is the same as writing:The result of this is a State computation, which has a helper function
.exec
that runs the computation with an initial state (outer0
) and returns the final state (discarding any result).If you want to avoid using State, you'll pretty much have to do it the way you started out. The whole point of State is to pass intermediate results between steps without explicitly mentioning them.