chimney icon indicating copy to clipboard operation
chimney copied to clipboard

Support for mapping multiple case classes

Open nsutcliffe opened this issue 5 years ago • 10 comments

Presuming I had the following:

case class Container1(a: Int)
case class Container2(b: String)
case class MergedContainers(a: Int, b: String)

It would be nice to do something like either of the following:

(Container1(1),Container2("HI")).transformInto[MergedContainers]

Or perhaps:

case class NestedContainers(container1: Container1, container2: Container2)
NestedContainers(Container1(1),Container2("HI")).transformInto[MergedContainers]

nsutcliffe avatar Jul 11 '19 09:07 nsutcliffe

Having case class that combines containers (NestedContainer), this should be also achievable by first proposal: https://github.com/scalalandio/chimney/issues/100#issuecomment-480563946

krzemin avatar Jul 11 '19 10:07 krzemin

Do you think this will be implemented sometime soon? Had a need for it a few times and used hacks but I have a feeling I’ll have a blocker in the next month or two and this could really come in handy

ittaiz avatar Apr 03 '20 14:04 ittaiz

@ittaiz many people ask for this feature recently - I think it should be next top priority. But it requires a lot of time to design it correctly, so I can't guarantee any fixed time frame.

krzemin avatar Apr 03 '20 16:04 krzemin

Sure thing, I complete understand. I appreciate the intent and thank you for your work!

On Fri, 3 Apr 2020 at 19:14 Piotr Krzemiński [email protected] wrote:

@ittaiz https://github.com/ittaiz many people ask for this feature recently - I think it should be next top priority. But it requires a lot of time to design it correctly, so I can't guarantee any fixed time frame.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/scalalandio/chimney/issues/115#issuecomment-608530442, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAKQQF4ILFPXGK4BBOMBDG3RKYDPNANCNFSM4IA5QDOQ .

ittaiz avatar Apr 04 '20 04:04 ittaiz

Ah, yeah, this is the feature I've been looking for. I imagine it's on the hard side to implement. Many thanks for your work on chimney @MateuszKubuszok

michaelbilow avatar Dec 25 '23 18:12 michaelbilow

I have some idea how to do it, but it will be a significant amount of work.

I was thinking about prioritizing 1.0.0-RC first, to not delay it anymore, and then( later in 2024) starting to work on such merging transformation (it would allow us to internally reimplement Patchers as such transformer). The challenges for this feature are that:

  • it would have to merge values recursively because that's where lies its true usefulness
  • it would have to allows merging and transforming an arbitrary number of values
  • it would have to cooperate with all the existing overrides (.withField*, .enable*)
  • it would have to preserve the semantics of existing code, and make sure that existing tests will still pass

I have some an idea how to do it, but it would require refactoring a few sensitive pieces of code, writing a bit more pieces of a new code, and creating a tons of tests to avoid feature interaction bugs, because at this point it's really easy to write code which might look okayish, pass a simple test, compile (obviously) but provide an unexpected behavior when combined with some other feature.

So I am planning to work on it somewhere in 2024, but I have no idea how much time I'll have so I am not committing to any ETA.

MateuszKubuszok avatar Dec 25 '23 22:12 MateuszKubuszok

Yeah, I had gotten stuck on this issue about 4 years ago IIRC, and returned to the same problem recently. Below is my best effort to make something like this work today (leveraging some tools I'm sure you'd rather not add to chimney, and losing some of chimney's cleverness about failed transforms).

import io.scalaland.chimney.dsl._
import org.scalacheck.ScalacheckShapeless._
import org.scalacheck.Arbitrary


case class X(a: Int)
case class Y(b: Int)
case class Z(a: Int, b: Int)

val x = X(1)
val y = Y(2)
val z = Arbitrary.arbitrary[Z]
  .sample
  .get
  .patchUsing(x)
  .patchUsing(y)

michaelbilow avatar Dec 26 '23 16:12 michaelbilow