Sharing parts of case classes


I recently found myself writing a lot of case class definitions like this

case class A(x: Int, a: A)
case class B(x: Int, b: B)
case class C(x: Int, c: C)

So they have common part and custom part. Right now, AFAIK, we don’t have the mechanism that would allow us to share parts of a case class. In a perfect world, I would be able to write

case class Base(x: Int)
case class A(a: A) extends2 Base
case class B(b: B) extends2 Base
case class C(c: C) extends2 Base

and then leverage all the benefits of case classes, so valid toString, equals, hashcode and all the libs with special macro treatment for them.

Of course, extend cannot be used for that purpose, but we could probably come up with a new keyword.

The tricky part is instantiation, where in this case we have a constructor with 1 visible parameter but we need to provide 2. One solution would be the desugaring that will provide an artificial constructor which would be the concatenation of all the extended case classes, so it looks like

val x = C(???, 1)

But this sounds a little bit complex and may be hard to understand and hard for tooling to support.

This also reminds trait parameters a bit, but the drawback of those is that you have to repeat all the parameters in the final class constructor so you can pass them down (if I remember and understand them correctly).
Second thing is that I’m not sure if macros (like one for deriving json codecs) would be able to work with trait parameters, and this is one of the most important parts.



I realized that what I really want is a support of proper records, where order of fields doesn’t matter, available e.g. in typescrit. And such behavior would be part of it. Whatever we do with case classes is rather poor imitation.


Which of the following objects would you want to be equal and which not?

Base(1), A(1, 1), A(1, 2)


Neither of them. I would expect equality to be defined like one for Set[(name, value)] so set of fields with it’s values


Have you looked at shapeless records?


You can just create traits, then instantiate them via Factory in feature.scala. The memory layout of the created objects are more compact than shapeless’ HList based records.


BTW have you looked into reimplementing your macros in dotty?