case class and inheritance: how to offer different behaviour - scala

situation:
trait Operation {
def something: Double
}
trait OperationPlus { this: A =>
override def something: Double = x + y
}
trait OperationMinus { this: A =>
override def something: Double = x - y
}
case class A(x: Double, y: Double) { this: Operation =>
}
val a = new A(1.0, 2.0) with OperationPlus
println(a.something)
error:
class A cannot be instantiated because it does not conform to its self-type A with Operation
Also, i can't instantiate A.
I tried multiple different approaches, but none provided what i'm looking for. I don't want to use case class inheritance, or give up case classes, and ideally trait / self types / something else should do the trick. Any ideas?
Update
preferred solution
trait Operation { this: A =>
def something: Double
}
trait OperationPlus extends Operation { this: A =>
override def something: Double = x + y
}
trait OperationMinus extends Operation { this: A =>
override def something: Double = x - y
}
abstract case class A(val x: Double, val y: Double) extends Operation
val a = new A(1.0, 2.0) with OperationPlus
println(a.something)
val b = new A(1.0, 2.0) with OperationMinus
println(b.something)
possible solution 1:
trait Operation {
def x:Double
def y:Double
def something: Double
}
trait OperationPlus extends Operation {
override def something: Double = x + y
}
trait OperationMinus extends Operation {
override def something: Double = x - y
}
abstract case class A(val x: Double, val y: Double) extends Operation
By using conventional classes, simple trait inheritance and a self-type in the actual value is possible to define it and supply behaviour dynamically.
Unfortunately, I have to redefine the fields in the trait. I guess is a fair compromise. Would be interested to know if somebody knows of another approach.
Thanks

Not sure about your use case, but you need to define class A like this if you want to make it work:
abstract case class A(x: Double, y: Double) extends Operation
But I don't think is very idiomatic approach to use case classes. They are mostly used as data containers and normally do not contain any behavior. (maybe you can tell some more information about things you want to achieve with this)

First, you should have:
trait OperationPlus extends Operation
trait OperationMinus extends Operation
Second, you cannot define A as case class, since that automatically defines the apply method on the companion object, where new A is called (with arguments). This call fails due to the wrong self type (the error you see).
Remove the case. If you need pattern matching, define an extractor yourself (implement unapply):
class A(val x: Double, val y: Double) { this: Operation => }
object A {
def unapply(v: A) = Some((v.x, v.y))
}

Related

How to read/write a generic class with upickle

Say I have the following trait:
trait T {
val x: Int
}
def foo(i: Int): T
I would like to bet able to write and read this trait using upickle without knowing what the specific class is. e.g.:
upickle.default.write(foo(3))
Such that I could elsewhere define foo to be something like:
case class A(x: Int)
def foo(i: Int): T = A(i)
I am thinking I need to define an implicit Writer as a member of T but I don't know what the appropriate syntax would be?
trait T {
val x: Int
}
object T {
implicit val rw: ReaderWriter[T] = ...
}
The problem is what to put into the ... part: if you have a T value, you can just store its x:
... = readwriter[Int].bimap[T](
t => t.x,
i => new T { val x = i }
)
The problem with this solution is that reading a written A(3) won't return an A. And this isn't really solvable without making T sealed or otherwise handling a specific set of subclasses only.
You could include a class name as well when writing, but that won't help if the class has any fields other than x to store:
class B(override val x: Int, y: String) extends T
If T is sealed, you just write
... = macroRW

Abstracting out function across value classes

Let's say I have the following code for value classes:
class Meters(val x: Int) extends AnyVal {
def +(m: Meters): Meters = new Meters(x + m.x)
}
class Seconds(val x: Int) extends AnyVal {
def +(s: Seconds): Seconds = new Seconds(x + s.x)
}
Is there any way for me to remove duplication of the "+" methods?
Something kind of like:
abstract class Units[T <: Units[T]](val x: Int) extends AnyVal {
def +(other: T): T = T(x + other.x)
}
Except I can't inherit from value classes, and I definitely can't use T like a constructor.
You can use a universal trait with a type class, lets start defining the trait.
trait Sum[T <: Sum[T]] extends Any {
val x: Int
def +(other: T)(implicit evidence : FromInt[T]): T = evidence.fromInt(x + other.x)
}
Now we need a type class that tell us how to go from an integer to some type, lets define this and call it FromInt
trait FromInt[T] {
def fromInt(x: Int) : T
}
now lets define the Meters value class which is as simple as
class Meters(val x :Int) extends AnyVal with Sum[Meters]
and in the companion object we can provide an implicit value of the type class we defined.
object Meters{
implicit val intConstructable : FromInt[Meters] = new FromInt[Meters] {
override def fromInt(x: Int) = new Meters(x)
}
}
and now we can just do
val added = new Meters(2) + new Meters(3)
println(added.x)

How to implement Ordered properly

I have a trait
trait Weight {
def getWeight: Int
}
Multiple classes inherits it, example:
case class Test(n: Int) extends Weight {
override def getWeight: Int = n
}
Now i want to add sorting ability to all Weight subclasses. I added Ordered to Weight:
trait Weight extends Ordered[Weight] {
def getWeight: Int
override def compare(that: Weight): Int = this.getWeight.compareTo(that.getWeight)
}
Try sorting:
val seq = Seq(Test(1), Test(4), Test(3), Test(2))
seq.sorted // error
And it's not compiles:
Error:(74, 6) diverging implicit expansion for type
scala.math.Ordering[A$A254.this.Test] starting with method $conforms
in object Predef seq.sorted;}
^
Whats i am doing wrong?
Another solution a bit different than mdm. Since sorted takes an implicit of Ordering, you can do the following:
seq.sorted(Ordering[Weight])
Your solution does not work because Ordered[T] is invariant in T, meaning that Ordered[Weight] has no relationship with Ordered[A]. You would need to specify that in the sub-classes.
You could use an implicit Ordering rather than an Ordered.
trait Weight{
def getWeight : Int
}
object Weight{
implicit def ordering[T <: Weight] : Ordering[T] = Ordering.by(w => w.getWeight)
}
case class A(w : Int) extends Weight{
def getWeight = w
}
case class B(w : Int) extends Weight{
def getWeight = w
}
import Weight._
Seq(A(1),B(2),B(0),A(3),A(-3)).sorted
Will result in:
List(A(-3), B(0), A(1), B(2), A(3))
Note that this solution relies on an Ordering[Int] to be available (which is, by default).

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
}
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
}

Scala: Convert nested value classes to Vector

I have some type safe case classes that i want to turn into a vector:
case class Bdrms(underlying: Double) extends AnyVal
object Bdrms {
implicit def toBdrm(x: Double): Bdrms = Bdrms(x)
}
case class Bath(underlying: Double) extends AnyVal
object Bath {
implicit def toBath(x: Double): Bath = Bath(x)
}
// same pattern for many (20-30) other variables. sq ft, price, etc.
// and a parent case class
case class RealEstateDataPoint(bdrm: Bdrm, bath: Bath, sqft: Sqft,//)
one service needs to use these Doubles in the form of a Vector. if value classes were not used, i.e. if we just had
case class RealEstateDatePoint(bdrm: Double, bath: Double...)
then something like the following works to iterate over the fields to create a vector:
def toVector(dataCC: RealEstateDataPoint): Vector[Double] = {
val m = dataCC.productArity
val vector = Vector(m)
for (i<-0 until m) {
vector(i) = dataCC.productElement(i).asInstanceOf[Double]
}
vector
}
But of course, the asInstanceOf type cast wont work with our value classes. Is there an idiomatic/efficient way to make the conversion to a vector?
You can use structural typing:
dataCC.productIterator.map {
case h:{ val underlying: Double } => h.underlying
}.toVector
You might also consider making all the case classes inherit some trait:
trait HasUnderlying { val underlying: Double }
case class Bdrms(val underlying: Double) extends HasUnderlying
And then you can get the desired vector as:
dataCC.productIterator.map {
case h: HasUnderlying => h.underlying
}.toVector