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
Related
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).
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
}
Given a polymorphic function, how to match the polymorphic argument and return a value of the same type without resorting to explicit casts?
sealed trait Data
case class DString(s: String) extends Data
case class DInt(n: Int) extends Data
def double[D <: Data](d: D): D = d match {
case DString(s) => DString(s ++ s)
case DInt(n) => DInt(n + n)
}
This produces type mismatches (found DString/DInt, required D).
Why does the type system not accept this when the input type clearly equals the output type?
This may be a legitimate place to use method overloading:
def double(ds: DString) = DString(ds.s ++ ds.s)
def double(di: DInt) = DInt(di.n + di.n)
You can also use type classes:
abstract class DataDoubler[A <: Data] {
def double(a: A): A
}
implicit object DStringDoubler extends DataDoubler[DString] {
def double(ds: DString) = DString(ds.s ++ ds.s)
}
implicit object DIntDoubler extends DataDoubler[DInt] {
def double(di: DInt) = DInt(di.n + di.n)
}
def double[A <: Data](a: A)(implicit dd: DataDoubler[A]): A = dd.double(a)
This seems like a reformulation of a problem that pops up time and again on StackOverflow, which is usually solved using F-bounded polymorphism. The only difference is that you want to preserve the type you're working on in a function that's not defined inside the trait.
So, what I'd do is move that double method inside the Data trait and use an abstract type member:
sealed trait Data {
type Self <: Data
def double: Self
}
case class DString(s: String) extends Data {
type Self = DString
def double = DString(s ++ s)
}
case class DInt(n: Int) extends Data {
type Self = DInt
def double = DInt(n + n)
}
Otherwise, I'm not aware of a solution.
{
class MyClass(name: String) {}
val x = new MyClass("x")
println(x.name) // Error name is not a member of MyClass
}
but
{
abstract class Base
case class MyClass(name: String) extends Base {}
var x = new MyClass("x")
println(x.name) // name is a member of MyClass
}
So, what's the deal with case classes? Why are all of the constructor parameters turned into variables.
name is member in both examples, but private in your first example while public in your second. Case classes make their constructor parameters public val by default.
Pattern matching is the most important but not the only application for case classes. Another important point is that they implement the equals and hashCode methods in terms of the constructor arguments (aka product elements). Therefore, case classes are very useful for defining data structures that serve as elements in sets or keys in maps. That in turn only makes sense if these elements are visible.
Compare:
class Foo(val i: Int)
val set1 = Set(new Foo(33))
set1.contains(new Foo(33)) // false!!
And:
case class Bar(val i: Int)
val set2 = Set(Bar(33)
set2.contains(Bar(33)) // true!
Two case class instances with equal parameters are equal themselves. You can imagine them representing some "constants". This implies that you should not have mutable state in them.
You can, however, use a second parameter list to exclude arguments from the equality:
case class Baz(i: Int)(val n: Long)
Baz(33)(5L) == Baz(33)(6L) // true!
Another useful feature which implies that the constructor arguments become values, is making copies. This is the way immutable data is changes—you create a new instance with a particular value changed, leaving the original value in place.
case class Person(name: String, age: Int)
val p1 = Person("Fuzzi", 33)
val p2 = p1.copy(age = 34)
The copy method uses default values for all unspecified argument, taking those values from the constructor args.
Just to be clear, the constructor arguments aren't used to create variables, they're used to create values.
If you specify val in your first example, the non-case class:
class MyClass(val name: String) {}
then you also get the argument translated into a public value, the same as is done for the case class.
In the example on the Scala-Lang site it says:
It makes only sense to define case classes if pattern matching is used
to decompose data structures. The following object defines a pretty
printer function for our lambda calculus representation:
followed by the example code:
object TermTest extends Application { def printTerm(term: Term) {
term match {
case Var(n) =>
print(n)
case Fun(x, b) =>
print("^" + x + ".")
printTerm(b)
case App(f, v) =>
Console.print("(")
printTerm(f)
print(" ")
printTerm(v)
print(")")
} } def isIdentityFun(term: Term): Boolean = term match {
case Fun(x, Var(y)) if x == y => true
case _ => false } val id = Fun("x", Var("x")) val t = Fun("x", Fun("y", App(Var("x"), Var("y")))) printTerm(t) println println(isIdentityFun(id)) println(isIdentityFun(t)) }
To add something to my comment due to lack of available space: consider the following example case class:
case class My(x: Int)
If you save it to file and pass it to scalac -print, you get following expanded code (I removed unimportant stuff):
case class My extends Object with Product with Serializable {
<caseaccessor> <paramaccessor> private[this] val x: Int = _;
<stable> <caseaccessor> <accessor> <paramaccessor> def x(): Int = My.this.x;
Notice <caseaccessor>s here.
And then companion object:
<synthetic> object My extends runtime.AbstractFunction1 with Serializable {
case <synthetic> def apply(x: Int): My = new My(x);
case <synthetic> def unapply(x$0: My): Option = if (x$0.==(null))
scala.this.None
else
new Some(scala.Int.box(x$0.x()));
case <synthetic> <bridge> def apply(v1: Object): Object = My.this.apply(scala.Int.unbox(v1));
//...
Notice apply and unapply here. If you look at complete output yourself, you'll learn more about how scala generates your code.
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))
}