Base class reference in Scala - scala

I have to store a set of shape classes (say squares and circles) in a single array/set in scala.
In C++, we can store pointers to objects of derived class in a base class pointer.
std::vector<shape*> list;
shape* temp = new square;
list.push_back(temp);
Is such a thing possible in Scala? If so how does that code look?

Scala is an OO language, so why would that be a problem?
trait Shape
case class Square(x: Int, y: Int, w: Int, h: Int) extends Shape
import scala.collection.mutable.ArrayBuffer
val list = new ArrayBuffer[Shape]
list += new Square(0,0,10,10)

In Scala, as in Java, array and list does not store actual elements, they only store references to actual elements(non primitives). That means you can do same thing with arrays.
Java arrays do store some actual elements of primitive data types. Scala 2.8+ has #specialized feature which prevents from boxing of "primitives"

Related

Why is an array of a value classes compiled to an array of objects?

As I understand, if you create an array of a value class, you're actually creating an array of objects rather than the wrapped primitive. What's the reason behind this?
source:
class Wrapper(val underlying: Int) extends AnyVal
class Main {
val i: Int = 1
val w: Wrapper = new Wrapper(1)
val wrappers = Array[Wrapper](new Wrapper(1), new Wrapper(2))
val ints = Array[Int](1, 2)
}
javap output:
public class Main {
public int i();
public int w();
public Wrapper[] wrappers(); // <----why can't this be int[] as well
public int[] ints();
public Main();
}
One of the constraints of value classes is that x.isInstanceOf[ValueClass] should still work correctly. Where correctly means: transparently, without the programmer having to be aware when values may or may not be boxed.
If an Array[Meter] would be represented as an Array[Int] at runtime the following code would not work as expected, because the information that the ints in the array are actually meters is lost.
class Meter(val value: Int) extends AnyVal
def centimeters[A](as: Array[A]) = as.collect{ case m: Meter => m.value * 100 }
Note that if you have val m = new Meter(42); m.isInstanceOf[Meter] then the compiler knows that m is a Meter even though it's an Int at runtime and he can inline the isInstanceOf call to true.
Also note that this wouldn't work for arrays. If you would box the values in the array on demand you'd have to create a new array, which wouldn't be transparent to the programmer because arrays are mutable and use reference equality. It would also be a disaster for performance with large arrays.
According to https://docs.scala-lang.org/overviews/core/value-classes.html:
Allocation Summary
A value class is actually instantiated when:
a value class is treated as another type.
a value class is assigned to an array.
doing runtime type tests, such as pattern matching.
[...]
Another situation where an allocation is necessary is when assigning
to an array, even if it is an array of that value class. For example,
val m = Meter(5.0)
val array = Array[Meter](m)
The array here contains actual Meter instances and not just the underlying double primitives.
This has probably something to do with type erasure, or simply because you're creating an array of a specific type, which doesn't allow one type to be treated as another. In any case, it's a technical limitation.

How to give an implicit sort to this Scala class?

case class Point(x:Int, y:Int)
class Scatterplot(arr: ArrayBuffer[Point]){
val arrayOfNums = arr.sorted //no implicit ordering defined!
}
So far there is not much in my class, but I'd like to be able to sort arr. Is there a way I'd be able to set arrayOfNums to arr.sorted? I am not sure how to tell Scala to treat a Point (x,y) as a tuple, basically, which has implied ordering already.
And my second question: If I had an ArrayBuffer of Scatterplots, how could I give that implied sort ordering as well?
Point doesn't literally have an "implied ordering" in any sense the compiler will recognize. It's pretty easy to provide one, though:
case class Point(x: Int, y: Int)
object Point {
implicit val pointOrdering: Ordering[Point] = Ordering.by {
case Point(x, y) => (x, y)
}
}
This provides the lexicographic ordering by saying that you want to map points to (Int, Int) (which does have an ordering instance) and compare those. If you want another ordering you can adjust the implementation appropriately.
Note that putting the ordering instance for Point in Point's companion object ensures that it will be available even if you haven't imported pointOrdering into scope explicitly.
To answer your follow-up question: you can use similar code for Scatterplot once you have an ordering for Point:
import scala.collection.mutable.ArrayBuffer
class Scatterplot(arr: ArrayBuffer[Point]){
val arrayOfNums = arr.sorted
}
object Scatterplot {
implicit val scatterplotOrdering: Ordering[Scatterplot] =
Ordering.Implicits.seqDerivedOrdering[ArrayBuffer, Point].on[Scatterplot](
_.arrayOfNums
)
}
Sorting on a mutable collection is an even worse idea than sorting a mutable collection, though.

bencode Scala Any type

I am porting over code that was written in Python over to Scala.
This python code [0,{}] creates a list of an integer and a dictionary.
The dictionary in this case will have keys that are strings, but the values can either be an int or a string.
I want to do something similar in Scala. I believe I would have to do something like:
List[Any] = (<int>, scala.collection.mutable.Map[String, Any](...))
I plan to bencode this list so the underlying type of 'Any' matters. How do I change the types so that the list can be bencoded?
For the most part, using Any is not the right thing to do in Scala.
Since your problem is that the map values can be either Int or String, rather than trying to use their common type (Any), make your own new type hierarchy to represent the possibilities:
sealed trait BData
case class BInt(i: Int) extends BData
case class BString(s: String) extends BData
Now your map will have a type signature of Map[String, BData]
Using BData as the "underlying type" is better than using Any because there is no ambiguity to it. It has exactly two subclasses; compared with Any, where your values might be a List[(Foo, Bar)], for all the compiler knows.
I'll take dylan answer little further.
Scala have strong type system and you should use it.
When you are using Any which is like Object in java, you lose the type.
You should define the types and let the compiler work for you:
sealed trait BData[T] {
val value: T
}
implicit class BInt(val value: Int) extends BData[Int]
implicit class BString(val value: String) extends BData[String]
the 'implicit' declaration makes the compiler "Convert" the types for you, so you can use it like this:
val bDataMap = scala.collection.mutable.Map[String, BData[_]]()
val l = List((1, bDataMap))
l(0)._2 += ("myKey1" -> "Str")
l(0)._2 += ("myKey2" -> 2)
l(0)._2("myKey1").value
and so on....

What would be an appropriate collection type for storing few elements?

Of the different collection types supported in Scala (lists, maps, hashmaps, set etc) what would be an appropriate collection type for implementing something that can be done by C code below
typedef enum { GOOD BAD MAX_QUALITY } quality
struct student_data s_data[MAX_QUALITY];
The collection size is small... 2 or 3 elements, but having a collection helps to keep the code elegant, when performing similar operations on the data .
Thanks!
List or Seq of different case classes should do the trick. When I say different case classes, I really mean:
case class CaseClass1(arg1: String, arg2: Int, arg3: OtherCaseClass)
case class OtherCaseClass(arg1: String, arg2: String)
val foo: List[CaseClass1] = ...
Then instances of CaseClass1 are composed and stored in your list.
If your goal is to create an enum-like data structure which provides a fast index lookup, I would go for:
sealed trait Quality { val index: Int }
case class BAD() extends Quality { val index = 0 }
case class GOOD() extends Quality { val index = 1 }
case class MAX_QUALITY() extends Quality { val index = 2 }
This allows to use pattern matching on an arbitrary quality: Quality and the verbose syntax quality.index makes it explicit that quality is used as an Int index at that point.
Can you please elaborate further ?
If your intention is to create structure in which you associate to something of type quality a data structure of type student_data then you can use a Map
Depending on whether you need mutable access, or you just create the structure and then the access is read-only you would use mutable or immutable maps.
I suggest, in your case, to use immutable Maps. The Scala Collection Library provides specialized versions for small maps, up to 5 inserted key-value pairs, which provide better performance and memory footprint than the unspecialized types of map.
val x = Map(1 -> 2, 2 -> 3)
println(x.getClass)
>> class scala.collection.immutable.Map$Map2
You can also define an alias, to work more easily with your structures
type StudentStruct = Map[Quality, StudentData]
Usage:
val studentStruct = Map(Bad -> studentData)
val studentStruct2 = studentStruct + (Good -> studentData2)
Hope it helps.

Getting implicit scala Numeric from Azavea Numeric

I am using the Azavea Numeric Scala library for generic maths operations. However, I cannot use these with the Scala Collections API, as they require a scala Numeric and it appears as though the two Numerics are mutually exclusive. Is there any way I can avoid re-implementing all mathematical operations on Scala Collections for Azavea Numeric, apart from requiring all types to have context bounds for both Numerics?
import Predef.{any2stringadd => _, _}
class Numeric {
def addOne[T: com.azavea.math.Numeric](x: T) {
import com.azavea.math.EasyImplicits._
val y = x + 1 // Compiles
val seq = Seq(x)
val z = seq.sum // Could not find implicit value for parameter num: Numeric[T]
}
}
Where Azavea Numeric is defined as
trait Numeric[#scala.specialized A] extends java.lang.Object with
com.azavea.math.ConvertableFrom[A] with com.azavea.math.ConvertableTo[A] with scala.ScalaObject {
def abs(a:A):A
...remaining methods redacted...
}
object Numeric {
implicit object IntIsNumeric extends IntIsNumeric
implicit object LongIsNumeric extends LongIsNumeric
implicit object FloatIsNumeric extends FloatIsNumeric
implicit object DoubleIsNumeric extends DoubleIsNumeric
implicit object BigIntIsNumeric extends BigIntIsNumeric
implicit object BigDecimalIsNumeric extends BigDecimalIsNumeric
def numeric[#specialized(Int, Long, Float, Double) A:Numeric]:Numeric[A] = implicitly[Numeric[A]]
}
You can use RĂ©gis Jean-Gilles solution, which is a good one, and wrap Azavea's Numeric. You can also try recreating the methods yourself, but using Azavea's Numeric. Aside from NumericRange, most should be pretty straightforward to implement.
You may be interested in Spire though, which succeeds Azavea's Numeric library. It has all the same features, but some new ones as well (more operations, new number types, sorting & selection, etc.). If you are using 2.10 (most of our work is being directed at 2.10), then using Spire's Numeric eliminates virtually all overhead of a generic approach and often runs as fast as a direct (non-generic) implementation.
That said, I think your question is a good suggestion; we should really add a toScalaNumeric method on Numeric. Which Scala collection methods were you planning on using? Spire adds several new methods to Arrays, such as qsum, qproduct, qnorm(p), qsort, qselect(k), etc.
The most general solution would be to write a class that wraps com.azavea.math.Numeric and implements scala.math.Numeric in terms of it:
class AzaveaNumericWrapper[T]( implicit val n: com.azavea.math.Numeric[T] ) extends scala.math.Numeric {
def compare (x: T, y: T): Int = n.compare(x, y)
def minus (x: T, y: T): T = n.minus(x, y)
// and so on
}
Then implement an implicit conversion:
// NOTE: in scala 2.10, we could directly declare AzaveaNumericWrapper as an implicit class
implicit def toAzaveaNumericWrapper[T]( implicit n: com.azavea.math.Numeric[T] ) = new AzaveaNumericWrapper( n )
The fact that n is itself an implicit is key here: it allows for implicit values of type com.azavea.math.Numeric to be automatically used where na implicit value of
type scala.math.Numeric is expected.
Note that to be complete, you'll probably want to do the reverse too (write a class ScalaNumericWrapper that implements com.azavea.math.Numeric in terms of scala.math.Numeric).
Now, there is a disadvantage to the above solution: you get a conversion (and thus an instanciation) on each call (to a method that has a context bound of type scala.math.Numeric, and where you only an instance of com.azavea.math.Numeric is in scope).
So you will actually want to define an implicit singleton instance of AzaveaNumericWrapper for each of your numeric type. Assuming that you have types MyType and MyOtherType for which you defined instances of com.azavea.math.Numeric:
implicit object MyTypeIsNumeric extends AzaveaNumericWrapper[MyType]
implicit object MyOtherTypeIsNumeric extends AzaveaNumericWrapper[MyOtherType]
//...
Also, keep in mind that the apparent main purpose of azavea's Numeric class is to greatly enhance execution speed (mostly due to type parameter specialization).
Using the wrapper as above, you lose the specialization and hence the speed that comes out of it. Specialization has to be used all the way down,
and as soon as you call a generic method that is not specialized, you enter in the world of unspecialized generics (even if that method then calls back a specialized method).
So in cases where speed matters, try to use azavea's Numeric directly instead of scala's Numeric (just because AzaveaNumericWrapper uses it internally
does not mean that you will get any speed increase, as specialization won't happen here).
You may have noticed that I avoided in my examples to define instances of AzaveaNumericWrapper for types Int, Long and so on.
This is because there are already (in the standard library) implicit values of scala.math.Numeric for these types.
You might be tempted to just hide them (via something like import scala.math.Numeric.{ShortIsIntegral => _}), so as to be sure that your own (azavea backed) version is used,
but there is no point. The only reason I can think of would be to make it run faster, but as explained above, it wont.