0

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?

Cigogne Eveillée
  • 2,178
  • 22
  • 36

1 Answers1

0

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.

Peter Neyens
  • 9,770
  • 27
  • 33
  • Thanks, super helpful! One last hurdle is that I'm getting the following compilation error: `could not find implicit value for parameter update: cmd23.UpdateRepr[cmd19.moreData1,R] copyOver(myData, otherData)` when calling copyOver. Any idea why? – Cigogne Eveillée Jul 16 '16 at 18:00
  • If you are trying on the REPL, try using `Ctrl + P` with the sealed traits, case classes and `UpdateRepr` in one go. – Peter Neyens Jul 16 '16 at 18:20
  • Ah, yes it works when copying all in one go in paste mode. Thanks! Last question: if I understand `UpdateRepr` correctly, `baseData` has to be an exact subset of `moreData` for this to work. Meaning `baseData` cannot have any field not present in `moreData`. Would you agree? – Cigogne Eveillée Jul 16 '16 at 22:12