[Scala]properly reading an object from a file in the presence of type erasure - scala

Let's say I have a map stored on disk and I should like to retrieve it:
type myType = Map[something , somethingElse]
...
try{
val bytes = Files.readAllBytes(path)
val is = new ObjectInputStream(new ByteArrayInputStream(bytes))
val m = is.readObject().asInstanceOf[myType]
Some(m)
}catch{
case _:FileNotFoundException | _:IOException | _:ClassCastException => None
}
So far so good. However, as Maps are generic and due to the ever-annoying type erasure, I doubt I can conveniently rely on the ClassCastException to make sure that if I ever change myType, outdated maps will be discarded.
The thought has crossed my mind to simply hash myType and to retrieve and compare the hash prior to retrieving the map, but that feels more like a workaround than a solution. What would be the proper way to handle this?
Edit:
The maps were stored to disk as follows:
var myMap : myType = ...
...
try{
val b = new ByteArrayOutputStream()
val os = new ObjectOutputStream(b)
os.writeObject(myMap)
Files.write(path, b.toByteArray)
}catch{
...
}

Have you looked into Manifests?
There is a good topic on here that explores getting around type erasure already: How do I get around type erasure on Scala? Or, why can't I get the type parameter of my collections?

Serializing any generic type in Java/Scala will suffer from type erasure.
There is a way to go around this using reflection and ClassTags (backed up with implicit conventions) but it assumes that the compiler has access to the initial types to be preserved. In the case of de-serializing an object from cold-storage, the compiler has no access to the initial type and it won't be able to help you.
Doing something similar to what you describe might be the way to go.

Related

Converting object of type Any into myClass, by passing the myClass as parameter

So I have a class:
case class Document (idx: String, name: String, code: String)
Due to some transformations, an object which was initially created as Document, now becomes of type Any.
val doc = Document("12", "anyName", "ps") // Ends up as type Any
So I want to convert it into type Document again.
I know that can be done like this:
val docNew = doc.asInstanceOf[Document]
But, what I am trying is to pass the type, in this case Document, as a parameter to make it more generic.
So I was trying the following:
val mType = Document
val docNew = doc.asInstanceOf[mType]
But Intellij says:
Cannot resolve symbol mType
Edit: My ultimate goal is to pass the parameter Document to a function, so at the end I could do something like:
def convertIntoDoc(doc: Any, mType: Type) = {
val docNew = doc.asInstanceOf[mType]
docNew
}
If you want to learn programming with Scala you have to learn about the difference between types and values.
Due to type-erasure type parameters never actually make it into your program. In reality, types only exist to help you write good code.
If you go down this road you will eventually realise that asInstanceOf[T] does not actually do anything.
You might think this is weird and with type-erasure the type system is superfluous, but let me assure you it is perfectly useful and when it comes to more complicated code actually becomes the stepping stone to generic programming, e.g. code that can be used in many different ways due to type parametrisation.
To spin this a little further you should almost never end up with a val of type Any because you will loose this additional safety net of static typing. This means you have already made a mistake somewhere upstream in your code.
Your ultimate goal is simple to achieve:
def convertIntoDoc[MType](doc: Any) = {
val docNew = doc.asInstanceOf[MType]
docNew
}
You just have to remember that MType is a type and not a variable. So it has to be used as type parameter and not as value parameter.
The problem is that casting an Any into a MType will get you ClassCastExceptions (when running your program!) if you use the wrong type.
asInstanceOf is very dangerous because it kind of overwrites the type safety that the Scala compiler provides.
If you have any questions about this let me know.
The correct way to convert your Any to Document is to use match:
val docNew = doc match { case d: Document => d }
This is safe because it will throw a MatchException if for some reason the object is not of type Document.
Your convertIntoDoc function is just a wrapper around asInstanceOf, so you need to give more detail on what this function is intended to do (preferably in a separate question).
instead of val you can use type
type mType = Document
val docNew = doc.asInstanceOf[mType]
For the second part of the question, you can pass the type using type parameter as argument
def convertIntoDoc[A](doc: Any) = {
val docNew = doc.asInstanceOf[A]
docNew
}

Expression of type scala.collection.Set[scala.Predef.String] doesn't conform to expected type scala.Predef.Set[scala.Predef.String]

I'm relatively new to programming and am enjoying using Scala to teach myself. I've come across a problem that I can't seem to wrap my head around.
Here is the snippet of code I'm trying to work on. All the Maps used are mutable.Map[String, Any]
def CreateCompleteVoterSet(): Set[String] =
{
val firstSet = concedeVoterMap.keySet.diff(voterMap.keySet)
val secondSet = emoteVoterMap.keySet.diff(voterMap.keySet)
val thirdSet = speedVoterMap.keySet.diff(voterMap.keySet)
var finalSet = voterMap.keySet ++ firstSet ++ secondSet ++ thirdSet
return finalSet
}
The error it gives me is: Expression of type scala.collection.Set[scala.Predef.String] doesn't conform to expected type scala.Predef.Set[scala.Predef.String]
I'm sure I could find some way to force it to the same type, possibly with toSet(), but I'm confused by what the error is. Could someone give me some insight on why the error is happening and point me in the right direction for a safe way to fix it?
Because there is no import for Set, Set[String] means scala.Predef.Set (scala.Predef._ is imported automatically in all Scala files). This is an alias for scala.collection.immutable.Set. But keySet method of scala.collection.mutable.Map returns a scala.collection.Set, which is a supertype of scala.collection.immutable.Set (and of scala.collection.mutable.Set, which isn't really relevant to the question). Combining scala.collection.Sets with ++ still gives you a scala.collection.Set.
The simple solution would be to change the return type to scala.collection.Set, unless you actually require a scala.collection.immutable.Set for other reasons.

Ambiguous Implicit Values when using HMap

HMap seems to be the perfect data structure for my use case, however, I can't get it working:
case class Node[N](node: N)
class ImplVal[K, V]
implicit val iv1 = new ImplVal[Int, Node[Int]]
implicit val iv2 = new ImplVal[Int, Node[String]]
implicit val iv3 = new ImplVal[String, Node[Int]]
val hm = HMap[ImplVal](1 -> Node(1), 2 -> Node("two"), "three" -> Node(3))
My first question is whether it is possible to create those implicits vals automatically. For sure, for typical combinations I could create them manually, but I'm wondering if there is something more generic, less boilerplate way.
Next question is, how to get values out of the map:
val res1 = hm.get(1) // (1) ambiguous implicit values: both value iv2 [...] and value iv1 [...] match expected type ImplVal[Int,V]`
To me, Node[Int] (iv1) and Node[String] (iv2) look pretty different :) I thought, despite the JVM type erasure limitations, Scala could differentiate here. What am I missing? Do I have to use other implicit values to make the difference clear?
The explicit version works:
val res2 = hm.get[Int, Node[Int]](1) // (2) works
Of course, in this simple case, I could add the type information to the get call. But in the following case, where only the keys are known in advance, I don't know how to do it:
def get[T <: HList](keys: T): HList = // return associated values for keys
Is there any simple solution to this problem?
BTW, what documentation about Scala's type system (or Shapeless or in functional programming in general) could be recommended to understand the whole topic better as I have to admit, I'm lacking some background for this topic.
The type of the key determines the type of the value. You have Int keys corresponding to both Node[Int] and Node[String] values, hence the ambiguity. You might find this article helpful in explaining the general mechanism underlying this.

Bypassing Set's invariance in scala

As also seen in the scaladoc, sets are invariant :( So what do you do when you wish treat one as if it were covariant?
I have a constructor object:
object SimpleGraph {
def apply[ID](vertices: List[AbstractVertex[ID]], edges: Set[AbstractEdge[ID]]) = {
val simpleGraph = new SimpleGraph[ID, AbstractVertex[ID], AbstractEdge[ID]]
vertices.map(simpleGraph.addNode)
edges.map(simpleGraph.addEdge)
simpleGraph
}
}
Given the invariance, sets of types that adhere to T <: AbstractEdge[ID] won't make it through into my apply function. The compilation error suggests using a wildcard type, which as much as I fathom means circumventing type safety altogether (?). My own solution is ugly because it uses cpu cycles and more memory to get over the invariance: changing the prototype's type from Set to List and converting back and forth between a list and a set to get the data through.
Have you got a better suggestion?
Thanks!
I am not terribly sure about it, because I am currently not able to test it properly, But wouldn't it work to tell your apply the exact type of the items in the set by type parameter?
object SimpleGraph {
def apply[ID, EDGE <: AbstractEdge[ID]](vertices: List[AbstractVertex[ID]], edges: Set[EDGE]) = {
val simpleGraph = new SimpleGraph[ID, AbstractVertex[ID], AbstractEdge[ID]]
vertices.foreach(simpleGraph.addNode)
edges.foreach(simpleGraph.addEdge)
simpleGraph
}
}

Possible to find parameter type methods return type in Scala where parameter is a primitive type?

Suppose I have:
class X
{
val listPrimitive: List[Int] = null
val listX: List[X] = null
}
and I print out the return types of each method in Scala as follows:
classOf[ComplexType].getMethods().foreach { m => println(s"${m.getName}: ${m.getGenericReturnType()}") }
listPrimitive: scala.collection.immutable.List<Object>
listX: scala.collection.immutable.List<X>
So... I can determine that the listX's element type is X, but is there any way to determine via reflection that listPrimitive's element type is actually java.lang.Integer? ...
val list:List[Int] = List[Int](123);
val listErased:List[_] = list;
println(s"${listErased(0).getClass()}") // java.lang.Integer
NB. This seems not to be an issue due to JVM type erasure since I can find the types parameter of List. It looks like the scala compiler throws away this type information IFF the parameter type is java.lang.[numbers] .
UPDATE:
I suspect this type information is available, due to the following experiment. Suppose I define:
class TestX{
def f(x:X):Unit = {
val floats:List[Float] = x.listPrimitive() // type mismatch error
}
}
and X.class is imported via a jar. The full type information must be available in X.class in order that this case correctly fails to compile.
UPDATE2:
Imagine you're writing a scala extension to a Java serialization library. You need to implement a:
def getSerializer(clz:Class[_]):Serializer
function that needs to do different things depending on whether:
clz==List[Int] (or equivalently: List[java.lang.Integer])
clz==List[Float] (or equivalently: List[java.lang.Float])
clz==List[MyClass]
My problem is that I will only ever see:
clz==List[Object]
clz==List[Object]
clz==List[MyClass]
because clz is provided to this function as clz.getMethods()(i).getGenericReturnType().
Starting with clz:Class[_] how can I recover the element type information that was lost?
Its not clear to me that TypeToken will help me because its usages:
typeTag[T]
requires that I provide T (ie. at compile time).
So, one path to a solution... Given some clz:Class[_], can I determine the TypeTokens of its method's return types? Clearly this is possible as this information must be contained (somewhere) in a .class file for a scala compiler to correctly generate type mismatch errors (see above).
At the java bytecode level Ints have to be represented as something else (apparently Object) because a List can only contain objects, not primitives. So that's what java-level reflection can tell you. But the scala type information is, as you infer, present (at the bytecode level it's in an annotation, IIRC), so you should be able to inspect it with scala reflection:
import scala.reflect.runtime.universe._
val list:List[Int] = List[Int](123)
def printTypeOf[A: TypeTag](a: A) = println(typeOf[A])
printTypeOf(list)
Response to update2: you should use scala reflection to obtain a mirror, not the Class[_] object. You can go via the class name if need be:
import scala.reflect.runtime.universe._
val rm = runtimeMirror(getClass.getClassLoader)
val someClass: Class[_] = ...
val scalaMirrorOfClass = rm.staticClass(someClass.getName)
// or possibly rm.reflectClass(someClass) ?
val someObject: Any = ...
val scalaMirrorOfObject = rm.reflectClass(someObject)
I guess if you really only have the class, you could create a classloader that only loads that class? I can't imagine a use case where you wouldn't have the class, or even a value, though.