Composing updates on different paths of an object
benhutchison opened this issue · 5 comments
I am experimenting with porting a closed source codebase from monocle to quicklens.
One issue I have hit 3 times thus far is the need to combine multiple updates to the same object, that occur along different paths. I have boiled it down to this simple example which illustrates (a) the problem, and (b) my current not-very-elegant attempt.
Currently doesn't work: Path must have shape: _.field1.field2.each.field3.(...), got: path1
case class Piece()
case class ChessBoard(inPlay: Set[Piece], captured: Set[Piece]) {
def capture(piece: Piece): Unit = {
require(inPlay.contains(piece))
require(!captured.contains(piece))
modify2(this)(_.inPlay, _ - piece)(_.captured, _ + piece)
}
}
def modify2[T, U, V](obj: T)(path1: T => U, update1: U=>U)(path2: T => V, update2: V=>V): T = {
import com.softwaremill.quicklens._
val temp = modify(obj)(path1).using(update1)
modify(temp)(path2).using(update2)
}
Here's another syntax option using implicit wrappers, but alas it suffers from the same "loss of path shape" problem:
case class Piece()
case class ChessBoard(inPlay: Set[Piece], captured: Set[Piece]) {
def capture(piece: Piece): Unit = {
require(inPlay.contains(piece))
require(!captured.contains(piece))
modify(this)(_.inPlay).using(_ - piece).andModify(_.captured).using(_ + piece)
}
}
implicit class LensWrapper[A](a: A) {
def andModify[T](path: A => T) = com.softwaremill.quicklens.modify(a)(path)
}
How about new syntax for modify
using an implicit class?
this // or any object
.modify(_.inPlay).using(_ - piece)
.modify(_.captured).using(_ + piece)
.modify(...).using(...) // etc.
(this requires some changes to the macro, but I have a working version locally :) )
I like that syntax, keen to try the new version
Take a look at version 1.4.0 + example in the readme :)