Sharing parts of case classes


#1

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.

WDYT?


#2

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.


#3

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

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


#4

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


#5

Have you looked at shapeless records?


#6

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.


#7

BTW have you looked into reimplementing your macros in dotty?


#8

For the record (no pun intended), there is couple of links relevant to the discussion and having proper record types in dotty:

The most recent (AFAIK) work on records for scala can be seen in this ICFP presentations: https://www.youtube.com/watch?v=ntrSagXL200
which is based on this paper: http://www.csc.kth.se/~phaller/doc/karlsson-haller18-scala.pdf

This implementation adds full features records to dotty. I have no idea if authors are reading this forum or if they are in contact with EPFL but it would be really nice to have this extension in dotty.

There is also some older threads/discussions here:


and what is currently available (and is a base for work from first links): https://dotty.epfl.ch/docs/reference/changed/structural-types.html

And I have looked at
shapeless records and scala-records but neither of them is adopted or highly usable (you can see why in the first presentation, which also compares them).


Proposal for programmatic structural types