jbrown
jbrown

Reputation: 7986

Scala generics: How to declare that a type must be a case class?

I have several case classes with a count field. It's 1 by default, and I have a reduce in my code that groups duplicates and sums that value to find the number of each object. E.g.:

case class Person(name: String, count = 1)
personList.groupBy(_.name).reduce((x,y) => x.copy(count = x.count + 1))

I have this logic in several case classes, and since my logic is a bit more complicated than the example above I want to create a generic merging function.

So I've created a sealed trait with a count field. I've then changed my case classes to extend from this, e.g.:

case class Person(name: String, override val count) extends Countable

So far, so good.

However, I can't work out how to declare my merge function so that it only accepts case classes that extend Countable. Because of that, it can't find the copy method.

Here's what I have:

  def merge[T <: Countable](f: T => Seq[String])(ms: Seq[T]): Vector[T] = 
    ms.groupBy(x => f(x).mkString("_")).mapValues(_.reduce { (x,y) => 
     x.copy(count = x.count + 1)        // can't find `copy`
  }).values.toVector

Is there a typeclass that I can also include that means a type has a copy method (or is a case class) using Scala 2.11.7?

Update:

Countable trait is:

sealed trait Countable {

  def timesSeen: Long = 1
}

Upvotes: 4

Views: 2796

Answers (1)

vvg
vvg

Reputation: 6385

How did you defined you Countable trait. Following snippet works fine for me:

  trait Countable[Z] {
    def count: Int
    def copy: Z
  }

  case class Person(name: String, override val count: Int) extends Countable[Person] {
    override def copy: Person = this
  }

  def merge[T <: Countable[T]](f: T => Seq[String])(ms: Seq[T]): Vector[T] = {
    val r = ms.groupBy(x => f(x).mkString("_")).mapValues(_.reduce { (x, y) =>
      x.copy
    }).values.toVector
    r
  }

Upvotes: 2

Related Questions