Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for mapping multiple case classes #115

Open
nsutcliffe opened this issue Jul 11, 2019 · 10 comments
Open

Support for mapping multiple case classes #115

nsutcliffe opened this issue Jul 11, 2019 · 10 comments
Labels
dragons ahead Task which requires handwriting compiletime reflection for Scala2&3 and/or updating the architecture enhancement

Comments

@nsutcliffe
Copy link

nsutcliffe commented Jul 11, 2019

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]
@krzemin
Copy link
Member

krzemin commented Jul 11, 2019

Having case class that combines containers (NestedContainer), this should be also achievable by first proposal: #100 (comment)

@ittaiz
Copy link

ittaiz commented Apr 3, 2020

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

@krzemin
Copy link
Member

krzemin commented Apr 3, 2020

@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.

@ittaiz
Copy link

ittaiz commented Apr 4, 2020 via email

@michaelbilow
Copy link

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

@MateuszKubuszok
Copy link
Member

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.

@michaelbilow
Copy link

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)

@sercanersoy
Copy link

Would be great to have this feature, exactly what I'm looking for. Any updates on this?

@MateuszKubuszok
Copy link
Member

MateuszKubuszok commented May 17, 2024

Not really. To implement it we'd have to:

  • update the DSL to allow having several source values - we cannot just use tuples, because (a, b, c).transformInto[CaseClass] is already reserved for a tuple transformation
    • to keep the API backward compatible probably something like foo.into[Bar].withFallbackValue(baz).transform would be needed, where 1 value is the "main" one, and unbounded number of fallbacks is provided
    • to keep it actually useful it would have to merge values recursively - that would also allows rewriting Patchers as a special case of patch.into[Obj].withFallbackValue(obj).transform - see Rewrite Patchers as a special case of multiple case classes mapping #538
  • then we'd update the macro to store several source values
  • then update the macro to pass down the config storing several inputs, with extracting the right values recursively (so that e.g. (foo, bar) into bar would be able to figure out (foo.a, baz.a) into bar.a etc)
  • finally, these fallback values (their fields) would have to be used in product types
  • additionally some decisions, probably using new flags, would have to be provided for collections: should they be merged, or should the first matching source be used

All of that would have to be made in such a way that it would not break with special cases handling for:

  • Options
  • Collections
  • Eithers
  • enums
  • AnyVals
  • DSL for modifying nested overrides (e.g. withFieldConst(_.a.matchingSubtype[A1].list.everyItem.bOpt.matchingSome, ...))

which means that it would be probably 1-2 weeks of full-time development of PoC (and by full-time I mean 40h a week), next 2 for writing unit tests for every single case when things might get difficult, then another week or two for fixing errors.

While it's perfectly doable (and not a rocket science, it requires mostly an attention to detail and patience), I don't see the capacity (nor feel the urge) anytime soon to sink in another 1-2 month of free full-time work.

EDIT: Just to be clear, I would love Chimney to have this and other features, and I am very happy that people are interested in it, but I am simply too exhausted from the last several months and I would gladly let someone else drive the project.

@ahoy-jon
Copy link

ahoy-jon commented Sep 8, 2024

I was about to open a ticket for "just that". Thank you so much for doing Chimney!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
dragons ahead Task which requires handwriting compiletime reflection for Scala2&3 and/or updating the architecture enhancement
Projects
None yet
Development

No branches or pull requests

7 participants