How does generics work in scala REPL? - scala

I knew what type erasure is. so, i think scala REPL cannot detect a generic type exactly.
As i mentioned above, scala can't detect generic type in pattern matching like this:
case list: List[Int]
But when i declare List type value, scala detects what generic type is contained.
scala> val a = List(1,2,3)
a: List[Int] = List(1, 2, 3)
How is it possible?

val a = List(1,2,3)
This is equivalent to:
val a = List.apply[Int](1,2,3)
The result type of List.apply[Int](...) is List[Int] and therefore, type inferencer assigns this type to identifier a. This happens during compilation. The REPL does not "detect" the type in runtime.
This is different from a pattern match:
val a: Any = ...
a match {
case list: List[Int] => ...
}
Here, we have a value a, for which we don't have any type information. So we're trying to check what type it is, but now we're doing this in runtime. And here we indeed cannot determine the exact type. The best we can do here is to match against List[_].
Summarizing:
When you type some code in the REPL, it first gets compiled into bytecode and then, evaluated. Displayed type information comes from the compilation phase, so it doesn't suffer from type erasure.

When you write:
val a = List(1,2,3)
Scala uses type inference to find the closest matching type during compile time. Essentially it will rewrite it for you as:
val a: List[Int] = ...
It will use this parameter type information for compile time to type check your code and erase it afterwards so you'll get List[_] in your program. This is because JVM works this way - type erasure.
When you pattern match on a list during runtime it's type information is erased so any List would match. Scala compiler will warn you during compilation about it.
This works in the same way in REPL and in regular compile->run cycle.

Related

Scala weird behavior for type conversion in generic function

Could anyone explain why that happens?
scala> def as[T](v: Any) = Try(v.asInstanceOf[T])
as: [T](v: Any)scala.util.Try[T]
scala> as[Int]("Hello")
res0: scala.util.Try[Int] = Success(Hello)
scala> res0.map(_ + 1)
res1: scala.util.Try[Int] = Failure(java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer)
It's not about boxing because you could use your own type instead of Int and it behaves the same way.
Doesn't seem to be about by-name parameter either, because you could replace Try with Option and it will be the same.
I'm guessing it's about erasure, but maybe somebody could give a detailed explanation?
It's all in the scaladoc:
Note that the success of a cast at runtime is modulo Scala's erasure semantics. Therefore the expression 1.asInstanceOf[String] will throw a ClassCastException at runtime, while the expression List(1).asInstanceOf[List[String]] will not. In the latter example, because the type argument is erased as part of compilation it is not possible to check whether the contents of the list are of the requested type.
So, because T is erased, Try(v.asInstanceOf[T]) will not immediately throw a ClassCastException, because as far as the JVM knows, you have a Try[Any]. But as soon as you try to treat the contained type like an Int, you trigger the exception.

relation between variance and mutabilty / immutability in Scala

I am playing around with collections in Scala and found that mutable collections are defined as invariant and immutable collections are defined as covariant. What is the relation between variance and mutability / immutability in Scala?
class Array[T]
class List[+T]
I had found an easy explanation in SIA. Following is straight from there.
Mutable objects need to be invariant
A type parameter is invariant when it’s neither covariant nor contravariant. All Scala mutable collection classes are invariant. An example can explain why mutable objects need to be invariant. Because ListBuffer is mutable, it’s declared as invariant as follows:
final class ListBuffer[A] ...{ ... }
Because it’s declared as invariant, you can’t assign ListBuffer from one type to another. The following code will throw a compilation error:
scala> val mxs: ListBuffer[String] = ListBuffer("pants")
mxs: scala.collection.mutable.ListBuffer[String] =
ListBuffer(pants)
scala> val everything: ListBuffer[Any] = mxs
<console>:6: error: type mismatch;
found : scala.collection.mutable.ListBuffer[String]
required: scala.collection.mutable.ListBuffer[Any]
val everything: ListBuffer[Any] = mxs
Even though String is a subtype of scala.Any, Scala still doesn’t let you assign mxs to everything. To understand why, assume ListBuffer is covariant and the following code snippet works without any compilation problem:
scala> val mxs: ListBuffer[String] = ListBuffer("pants")
mxs: scala.collection.mutable.ListBuffer[String] =
ListBuffer(pants)
scala> val everything: ListBuffer[Any] = mxs
scala> everything += 1
res4: everything.type = ListBuffer(1, pants)
Can you spot the problem? Because everything is of the type Any, you can store an integer value into a collection of strings. This is a disaster waiting to happen. It’s exactly what happens to Java arrays. To avoid these kinds of problems, it’s always a good idea to make mutable objects invariant. The next question is what happens in case of an immutable object for collections. It turns out that for immutable objects, covariance isn’t a problem at all. If you replace ListBuffer with the immutable List, you can take an instance of List[String] and assign it to List[Any] with- out a problem.
scala> val xs: List[String] = List("pants")
xs: List[String] = List(pants)
scala> val everything: List[Any] = xs
everything: List[Any] = List(pants)
The only reason this assignment is safe is because List is immutable. You can add 1 to xs List, and it will return a new List of type Any.
scala> 1 :: xs
res5: List[Any] = List(1, pants)
Again, this addition is safe because the cons(::) method always returns a new List, and its type is determined by the type of elements in the List. The only type that could store an integer value and reference value is scala.Any. This is an important property to remember about type variance when dealing with mutable/ immutable objects.
The best way to understand contravariance is to see the problem that comes when it’s absent. Try to spot the problem in the following Java code example:
Object[] arr = new int[1];
arr[0] = "Hello, there!";
You end up assigning the string to an integer array. Java catches this error at runtime by throwing an ArrayStoreException. Scala stops these kinds of errors at compile time by forcing parameter types to be either contravariant or invariant.
Hope this helps.
A type can only be marked covariant if that type parameter only appears in covariant positions. Generally, this means that the class/trait/object has methods that returns values of the variant type but does not have methods with parameters of the variant type. Mutable collections always have methods with parameters of the variant type, e.g. update. Imagine what would happen if Array could be declared Array[+T]:
val as = Array[String]("a string")
// this statement won't typecheck in actual Scala
val aa: Array[AnyRef] = as
aa(0) = ("I'm a string...", "but this tuple itself isn't!")
// Tuples don't have a substring method, so this would fail at run-time
// if the compiler allowed this code to compile.
as(0).substring(0)

Why can a Map object be created without an apply-method?

C:\Users\John>scala
Welcome to Scala version 2.9.2 (Java HotSpot(TM) Client VM, Java 1.6.0_32).
Type in expressions to have them evaluated.
Type :help for more information.
scala> import scala.collection.mutable.Map
import scala.collection.mutable.Map
scala> Map()
res4: scala.collection.mutable.Map[Nothing,Nothing] = Map()
When using Map() without keyword new the apply method from the corresponding companion object will be called. But the Scala Documentation does not list an apply method for mutable Maps (only an apply method to retrieve a value from the map is provided).
Why is the code above still working ?
It looks like a bug in scaladoc. There is an apply method in object collection.mutable.Map (inherited from GenMapFactory) but it does not appear in the doc for Map. This problem seems to be fixed in the doc for upcomping 2.10.
Note : you must look into the object documentation, not the class one. The method apply in the class of course works with an existing map instance, and retrieve data from it.
There is an apply() method on the companion object of scala.collection.immutable.Map(). It is inherited from scala.collection.MapFactory . That method takes a variable number of pair arguments, and is usually used as
Map("foo"->3, "bar"->4, "barangus"->5)
Calling it with no arguments evidently works as well, but someone brighter than me would have to explain why the type inference engine comes up with scala.collection.mutable.Map[Nothing,Nothing] for it.
As sepp2k already mentioned in his comment the symbol Map refers to the companion object of Map, which gives you access to its single instance. In patter-matching this is often used to identify a message:
scala> case object Foo
defined module Foo
scala> def send[A](a: A) = a match { case Foo => "got a Foo" case Map => "got a Map" }
send: [A](a: A)String
scala> send(Map)
res8: String = got a Map
scala> send(Foo)
res9: String = got a Foo
If you write Map() you will call the apply method of object Map. Because you did not give any values to insert into the Map the compiler can't infer any type, thus it has to use the bottom type - Nothing - which is a subtype of every type. It is the only possible type to infer which will not break the type system although there are variances. Would Nothing not exist the following code would not compile:
scala> Map(1 -> 1) ++ Map()
res10: scala.collection.mutable.Map[Int,Int] = Map(1 -> 1)
If you take a look to the type signature of ++ which is as follows (source)
def ++[B1 >: B](xs: GenTraversableOnce[(A, B1)]): Map[A, B1]
you will notice the lower-bound type parameter B1 >: B. Because Nothing is a subtype of everything (and B in our case) the compiler can find a B1 (which is Int in our case) and successfully infer a type signature for our Map. This lower-bound is needed because B is covariant (source),
trait MapLike[A, +B, ...] ...
which means that we are not allowed to deliver it as a method parameter (because method parameters are in contravariant position). If the method parameters would not be in contravariant position Liskov's substitution principle would no longer kept by the type system. Thus to get the code to compile a new type (here called B1) has to be found.
As Didier Dupont already pointed out there are some bugs in Scaladoc 2.9, which are solved in 2.10. Not only some missed methods are displayed there but also methods added by an implicit conversion can be displayed (Array for example does display a lot of methods in 2.10 which are not displayed in 2.9).

Type extraction in Scala

I am pretty new to Scala and advanced programming languages. I try to solve the following problem.
I have got:
val s: Seq[SomeMutableType[_]]
I assume that all elements in the sequence are of the same type (but do not know which one at this point).
How may I call :
def proc[T](v0: SomeMutableType[T], v1: SomeMutableType[T]) { /* ... */ }
with something like
proc(s(0), s(1))
The compiler complains :
type mismatch; found : SomeMutableType[_$351] where type _$351 required:
SomeMutableType[Any] Note: _$351 <: Any, but class SomeMutableType is invariant in type T. You
may wish to define T as +T instead. (SLS 4.5)
I thought about that covariant thing, but I do not believe it makes sense in my case. I just want the compiler believe me when I say that s(0) and s(1) are of the same type! I usually do this via some casting, but I cannot cast to SomeMutableType[T] here since T is unknown due to erasure. Of course, I cannot change the definition of proc.
The problem is that you truly cannot make such a guarantee. For example:
scala> import scala.collection.mutable.Buffer
import scala.collection.mutable.Buffer
scala> val s: Seq[Buffer[_]] = Seq(Buffer(1), Buffer("a"))
s: Seq[scala.collection.mutable.Buffer[_]] = List(ArrayBuffer(1), ArrayBuffer(a))
See? You don't know that s(0) and s(1) are of the same type, because they may not be of the same type.
At this point, you should ask a question about what you want to accomplish, instead of asking how to solve a problem in how you want to accomplish it. They way you took won't work. Step back, think what problem you were trying to solve with this approach, and ask how to solve that problem.
For instance, you say:
I assume that all elements in the sequence are of the same type (but do not know which
one at this point).
It may be that what you want to do is parameterize a class or method, and use its type parameter when declaring s. Or, maybe, not have an s at all.
I am new to Scala, but as far as I can see your problem is the use of a wildcard type parameter when you declare s:
val s: Seq[SomeMutableType[_]]
As far as I understand, type erasure will always happen and what you really want here is a parameterised type bound to where s is initialised.
For example:
scala> class Erased(val s: List[_])
defined class Erased
scala> new Erased(List(1,2,3)).s.head
res21: Any = 1
If instead you use
scala> class Kept[T](val s: List[T])
defined class Kept
scala> new Kept(List(1,2,3)).s.head
res22: Int = 1
Then the contents of s retain their type information as it is bound to T. i.e. This is exactly how you tell the compiler "that s(0) and s(1) are of the same type".

What is the type that the Scala REPL prints?

When I create a small Map in the repl, it tells me that its type is immutable.Map
scala> Map(1->1, 2->2)
res8: scala.collection.immutable.Map[Int,Int] = Map((1,1), (2,2))
This isn't the whole truth though, as in this case
scala> Map(1->1, 2->2).getClass
res9: java.lang.Class[_] = class scala.collection.immutable.Map$Map2
How does Scala decide what to print as the type of an expression?
The short answer is that the Scala REPL prints the static type of your results, as inferred from your expression Map(1->1, 2->2), and getClass returns the dynamic type, which can be a subtype of the static type.
A longer answer would be about how Scala's type inference engine works. You'll maybe want to read the relevant sections in the Scala Language Reference.