Let's assume that we have a common trait Model.
trait Model {
def id: String
def updated: Date
}
And we have 2 case classes extending this trait.
case class C1(id: String, updated: Date, foo: String) extends Model
case class C2(id: String, updated: Date, bar: Int) extends Model
Is it possible to write a utility function like below which takes a Model as parameter and returns a copy with an updated value for the updated field?
object Model {
def update[T <: Model](model: T): T = {
model.copy(updated = new Date) // This code does not compile.
}
}
The "best" abstraction you can write here would be a Lens
which looks like:
trait Lens[A, B]{
def get: A => B
def set: (A, B) => A
}
so that your code looks like:
def update[A](that: A, value: Date)(implicit tLens: Lens[A, Date]): A =
tLens set (that, value)
Your code has two issues:
copy
is not defined on the trait, so you need to have something defined on the trait that you can use.update
to return a T
instead of a Model
, every Model
must know its actual subtype.You can fix it like this:
trait Model[T <: Model[T]] {
def id: String
def updated: Date
def withDate(d: Date): T
}
case class C1(id: String, updated: Date, foo: String) extends Model[C1] { def withDate(d: Date) = copy(updated = d) }
case class C2(id: String, updated: Date, bar: Int) extends Model[C2] { def withDate(d: Date) = copy(updated = d) }
object Model {
def update[T <: Model[T]](model: T): T = {
model.withDate(new Date) // This code does not compile.
}
}
So now it works:
scala> val c1 = C1("test", new Date, "foo")
c1: C1 = C1(test,Mon Apr 21 10:25:10 CDT 2014,foo)
scala> Model.update(c1)
res0: C1 = C1(test,Mon Apr 21 10:25:17 CDT 2014,foo)
copy
is a method defined on your case classes. Not on your base trait Model
. What if you have that:
trait Model {
def id: String
def updated: Date
}
case class C1(id: String, updated: Date, foo: String) extends Model
case class C2(id: String, updated: Date, bar: Int) extends Model
class NotACaseClass(val id: String, val updated: Date) extends Model
NotACaseClass
is a very valid child of Model
, and you could pass an instance of it to your update
function, but good luck finding a copy
method :)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With