Scala: Copying a generic case class into another

2019-08-22 05:08发布

问题:

I have the following setup, where I want to copy an instance of baseData into that of moreData:

sealed trait baseData {
  def weight: Int
  def priority: Int
} 

sealed trait moreData {
  def weight: Int
  def priority: Int
  def t: String
  def id: String
} 

case class data1(override val weight: Int, override val priority: Int) extends baseData 
case class moreData1 (override val weight:Int, override val priority: Int, override val t: String, override val id: String)extends moreData

So copying myData into otherData below:

val myData = data1(1,1) 
val otherData = moreData1 (2,2,"C","abcd") 

would yield: moreData1(1,1,"C","abcd").

To do this, I want to use a function with the following signature, because I will have more than one case class extending both baseData and moreData:

def copyOver[A <:baseData, B <:moreData](from: A, to: B) = {} 

I'm sure you can do this with Shapeless, but haven't figured out how. There are examples (here) on copying case classes extending a same trait, and others (here) mapping values between different case classes via generic representation. But I haven't figured out how to use LabelledGeneric with the trait-bounded arguments passed into copyOver. I also don't want to have to hardcode the extra fields in otherData that aren't present in myData.

I'm looking for a completely generic implementation. Any ideas?

回答1:

You should be able to use the UpdateRepr type class from your first shapeless example.

You could define copyOver using UpdateRepr as follows :

import shapeless._

// baseData, moreData, data1, moreData1
// UpdateRepr ...

def copyOver[A <: baseData, B <: moreData, R <: HList](
  from: A,
  to: B
)(implicit 
  lgen: LabelledGeneric.Aux[A, R],
  update: UpdateRepr[B, R]
): B = update(to, lgen.to(from))

Which you could use as follows :

val myData = data1(1,1) 
val otherData = moreData1(2,2,"C","abcd") 

copyOver(myData, otherData)
// moreData1 = moreData1(1,1,C,abcd)

Note that it is possible you run into problems with SI-7046, because of the sealed trait (Coproduct) type class derivation for UpdateRepr, which you could notice in the REPL or when splitting UpdateRepr and the sealed traits over multiple files.