Chaining Scalaz Lens set operations

357 views Asked by At

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))
1

There are 1 answers

0
Rev. C. Bennett Hoffman On

You can simplify the State monad version somewhat:

(leftTop := 11) >> (rightTop := 33) exec outer0

Or, if you prefer:

val modifier = (leftTop := 11) >> (rightTop := 33)
modifier.exec(outer0)

Your original State version looks a little weird since the <- in a for statement is just syntax sugar for calls to .flatMap. Simplifying things a bit, the result of leftTop := 11 has a type like State[Outer, Outer, Int], which is roughly equivalent to a function with type Outer => (Outer, Int). State keeps track of the Outer part of the result and passes the Int part to .flatMap. Since you don't care about the Int 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:

(leftTop := 11) flatMap (_ => rightTop := 33)

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.