-
-
Notifications
You must be signed in to change notification settings - Fork 205
Description
Hopefully my suggestion here is not too stupid. (This is my first day using more advanced scala like scalaz, monocle & some shapeless:)) - Also note: I have never written any macros for scala, so I don't know its limitations
Some background
Monocle is great - really makes creating permutations of immutable case class objects much easier. However the process is still rather verbose - Even with the @lenses macro. Compare for example the shapeless lens syntax (val myLens = lens[MyType].myfield.mynestedfield) - however IntelliJ really doesn't work well at all with Shapeless, so I end up using your Monocle lenses anyway.
The problem
For just updating nested case classes, the monocle lens composition syntax is a little verbose - and could be improved, see below for (possibly dumb :) ) suggestion on how this could work with the @lenses macro
Desired auto generated lens composition syntax (with proof of concept implementation below)
- A syntax similar in length to shapeless lenses, e.g. val myLens = MyType.myfield.mynestedfield
- A direct way to set fields, e.g. myInstance.set(myfield..mynestedfield, newValue)
Possible solution
I made some experiments with Monocle and concluded it is possible to implement syntax similar to (1) and (2) if generated type classes existed that made it easy to summon Companion objects (using the method from the original post by Miles Sabin).
Here is a manual example:
@Lenses case class Nested(foo : String)
@Lenses case class Address2(street : String, city : String, postcode : String, e: Nested)
@Lenses case class Person2(name : String, age : Int, address : Address2)
// If these could be generated with macros..
object Address2 {
implicit def aComp = new Companion[Address2] {
type C = Address2.type
def apply() = Address2
}
}
object Person2 {
implicit def pComp = new Companion[Person2] {
type C = Person2.type
def apply() = Person2
}
}
object Nested {
implicit def pComp = new Companion[Nested] {
type C = Nested.type
def apply() = Nested
}
}
If the above type class instances were generated by for example the @lenses macro, it is possible to implement something like the below (or better) without too much trouble (See links below for full POC source).
val person = Person2("Joe Grey", 37, Address2("Southover Street", "Brighton", "BN2 9UA", Nested("123")))
// Then we could do this!
val fooLens = Person2.address(_.e(_.foo))
fooLens.set("lalala")(person) shouldBe Person2("Joe Grey", 37, Address2("Southover Street", "Brighton", "BN2 9UA", Nested("lalala")))
// --> Or even this! <--
val p1 = person.set(_.name, "123")
val p2 = person.set(_.address(_.city), "dumbletown")
val p3 = person.set(_.address(_.e(_.foo)), "eeee")
p1 shouldBe Person2("123", 37, Address2("Southover Street", "Brighton", "BN2 9UA", Nested("123")))
p2 shouldBe Person2("Joe Grey", 37, Address2("Southover Street", "dumbletown", "BN2 9UA", Nested("123")))
p3 shouldBe Person2("Joe Grey", 37, Address2("Southover Street", "Brighton", "BN2 9UA", Nested("eeee")))
Question to you
Could we have a discussion on whether we could have something like a LensesCompanion type class with auto generated instances for all @lenses annotated case classes?
For the full POC code with tests that the above is taken from, see The last test here and/or The full POC implementation