2
votes

Given this code:

object Testy extends App {

  case class Person(
                     id: Option[Long],
                     firstName: String,
                     lastName: String,
                     address: Address)

  case class Address(id: Option[Long],
                     name: String,
                     number: Int)

  val personOrAddress:AnyRef= Person(Some(1L), "first", "last", Address(Some(1L), "street", 1))
  type HasCopyMethodWithId = _
  val newId = Some(123L)
  personOrAddress.asInstanceOf[HasCopyMethodWithId].copy(id = newId)
}

How can I implement 'type HasCopyMethodWithId' so that this code compiles and does not fail in runtime?

I've tried:

type HasCopyMethodWithId = {def copy(id: Option[Long]): AnyRef}
1
I think you can't, these copy methods are different. You would have to create another method like copyId that would use copy. - Ɓukasz
Could the actual type (not a general structural type) be detected via macros? - eirirlar

1 Answers

4
votes

The synthetic copy method provided by case classes is one single method with all arguments of that case class, not an individual overloaded one for id, firstName etc. Therefore the structural type doesn't match.

You could add an auxiliary method:

case class Person(id: Option[Long],
                  firstName: String,
                  lastName: String,
                  address: Address) {

  def copyId(newId: Option[Long]): Person = copy(id = newId)
}

case class Address(id: Option[Long],
                   name: String,
                   number: Int) {
  def copyId(newId: Option[Long]): Address = copy(id = newId)
}

val personOrAddress: Any = 
  Person(Some(1L), "first", "last", Address(Some(1L), "street", 1))

type HasCopyMethodWithId = { def copyId(id: Option[Long]): Any }
val newId = Some(123L)
personOrAddress.asInstanceOf[HasCopyMethodWithId].copyId(id = newId)

But then almost certainly better is to provide a static type:

trait CopyWithId { 
  type Repr 
  def copyId(id: Option[Long]): Repr
}

case class Person(id: Option[Long],
                  firstName: String,
                  lastName: String,
                  address: Address) extends CopyWithId {

  type Repr = Person
  def copyId(newId: Option[Long]): Person = copy(id = newId)
}

case class Address(id: Option[Long],
                   name: String,
                   number: Int) extends CopyWithId {

  type Repr = Address
  def copyId(newId: Option[Long]): Address = copy(id = newId)
}

val personOrAddress: CopyWithId = 
  Person(Some(1L), "first", "last", Address(Some(1L), "street", 1))

val newId = Some(123L)
personOrAddress.copyId(id = newId)