3

Suppose I have some abstract value field defined in a trait:

trait Base {
  val toBeOverride: String
}

case class Impl(other:Int) extends Base {
  override val toBeOverride = "some value"
}

How can I write a function that I can easily get a cloned instance only overriding the toBeOverride value, like this:

// copy only available to case class instance
// v does not have method 'copy'
def overrideBaseValue[T <: Base](v: Base) = 
    v.copy(toBeOverride = "prefix" + v.toBeOverride) 

?

Edit

@som-snytt, I don't think this is a duplicate, just like a Trait is not the same as an Abstract Class. And the answers of that question do not satisfy me, see below.

@Blaisorblade, yes, it is a problem. For instances of each sub case class, the toBeOverride field are the same, so it should not appear in the constructor.

For now all the suggestions are to define an customized copy method in each(!) sub case class and that in my opinion is ugly and shows the incapability of the language.

xiefei
  • 6,563
  • 2
  • 26
  • 44
  • 1
    Do you need the case class? Can you just create a new instance of the Base trait with the toBeOverride method overriden. Like this: `def overrideBaseValue(v: Base) = new Base {def toBeOverride = "prefix" + v.toBeOverride }` – Ratan Sebastian Dec 09 '12 at 16:54
  • Duplicate of http://stackoverflow.com/questions/12370244/case-class-copy-method-with-superclass – som-snytt Dec 10 '12 at 01:12
  • @rjsvaljean, yes, when I say "copy" I want to preserve all the other fields of the case class instances, e.g. `other` in the example. – xiefei Dec 10 '12 at 01:56
  • @ziggystar Please, humiliate me with **facts**, shut me up with **solutions**. – xiefei Dec 11 '12 at 02:08

3 Answers3

1

The simplest solution is to just add the method you want to Base:

trait Base {
  val toBeOverride: String
  def copyBase(newToBeOverridden: String): Base
}

case class Impl(other:Int, override val toBeOverride: String = "some value") extends Base {
  def copyBase(newToBeOverridden: String) = copy(toBeOverride = newToBeOverridden)
}

This also allows to directly create an instance of Impl while specifying the value of toBeOverride (which wasn't possible). The only disadvantage is that now pattern matches using Impl have to change syntax - please update your question and add a comment if that's a problem.

BTW, if you just want to add a prefix (as in your example), that's no problem:

case class Impl(other:Int, override val toBeOverride: String = "some value") extends Base {
  def copyBase(newToBeOverridden: String) = copy(toBeOverride = toBeOverride + newToBeOverridden)
}
Blaisorblade
  • 6,438
  • 1
  • 43
  • 76
1

Here are two mechanisms.

Apparently, in the near future you'll be able to write a macro that can emit the anonymous subclass, but until then, I think this typeclass is not arduous.

Just kicking the tires on Dynamic here.

import scala.language.dynamics
import scala.reflect._
import scala.reflect.runtime.{ currentMirror => cm }
import scala.reflect.runtime.universe._

trait Base {
  def m: String
}
case class Impl(p: Int) extends Base {
  override val m = "some value"
}

trait Basic extends Dynamic {
  protected def m: String
  def selectDynamic(f: String): Any =
    if ("m" == f) m else reflecting(this, f)
  protected def reflecting(b: Basic, f: String) = {
    val im = cm.reflect(b)
    val member = im.symbol.typeSignature member newTermName(f)
    require(member != NoSymbol, s"No such member $f")
    (im reflectMethod member.asMethod)()
  }
}
case class Implic(p: Int) extends Basic {
  override protected val m = "some value"
}

object Test extends App {

  implicit class Copy[A <: Base](val b: A) {
    def overriding(overm: String): A = (b match {
      case impl: Impl => new Impl(impl.p) { override val m = overm }
      case b: Base    => new Base { override val m = overm }
    }).asInstanceOf[A]
  }
  implicit class Proxy[A <: Basic : ClassTag](val b: A) {
    def proximately(overm: String): Basic = new Basic {
      override val m = overm
      override def selectDynamic(f: String): Any =
        if ("m" == f) overm else reflecting(b, f)
      override def toString = b.toString
    }
  }

  // asked for this
  //def overriding[T <: Base](v: Base) = v.copy(m = "prefix" + v.m)

  /* want something like this
  def overriding[T <: Base](v: Base) = new Impl(v.p) {
    override val m = "some value"
  } */

  val a = Impl(5)
  val b = a overriding "bee good"
  Console println s"$a with ${a.m} ~> $b with ${b.m}"
  // or
  val c = Implic(7)
  val d = c proximately "dynomite"
  Console println s"$c with ${c.m} ~> $d with ${d.m}"
}
som-snytt
  • 39,429
  • 2
  • 47
  • 129
  • Thanks. I will not consider the `copy` version. The `Dynamic`+`reflection` version is good but it seems that any access to d except d.m will have return type `Any` which is not perfect. – xiefei Dec 10 '12 at 08:58
0

Since traits don't get copy methods automatically, you can try using a Base case class instead:

  case class Base(toBeOverride: String)

  case class Impl(other: Int, someVal: String = "some value") extends Base(someVal)

  def overrideBaseValue[T <: Base](v: Base) =
      v.copy(toBeOverride = "prefix" + v.toBeOverride)

The problem that you're going to run into though, is that copy returns an instance of Base and I don't think that you can convert it back to your original Impl class. For instance, this won't compile:

  def overrideBaseValue[T <: Base](v: T): T =
      v.copy(toBeOverride = "prefix" + v.toBeOverride)
Noah
  • 13,821
  • 4
  • 36
  • 45