When I was writing my recent answer I also tried to solve the problem in more "functional" way, but stuck with the following problem:
scala> "1".asInstanceOf[Int]
java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
at scala.runtime.BoxesRunTime.unboxToInt(Unknown Source)
...
but
scala> Some("1".asInstanceOf[Int])
res29: Some[Int] = Some(1)
and only
scala> res29.get
java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
at scala.runtime.BoxesRunTime.unboxToInt(Unknown Source)
...
It looks like the Some's argument is lazy evaluated, but I can't find any clue in the sources. The x parameter in the Some constructor is strict.
Why does Some together with asInstanceOf has such a strange behavior?
The constructor argument is not lazily evaluated. The moment you get the error is the moment when the REPL try to display the result, as an Int (unboxToInt). If you look further in the stack, you find scala_repl_result.
I believe the problem is that asInstanceOf[Int] does no check at all at runtime. I don't know whether it is according to spec (for value types) or a bug. The compiler is tricked into accepting that "1" is an Int (or boxed Int), pending a runtime check implied by asInstanceOf, but which will not happen.
As Option/Some are not specialized, at runtime there is only Some[Object]. So for the JVM, there is a call to a constructor new Some(Object o), which is accepted at code verification and at runtime. The toString (called by the REPL on the freshly built Some) is inside the generic code, where T is treated as Object too (or AnyRef), so it works, and Some(1) is displayed. On the other hand, every time there is an access to the value in the context where the compiler knows the type of the generic parameter, a cast is inserted in the code (+ unboxing in case of a value type). This is when it actually fails (here REPL does unboxing before displaying).
Edit This was in Scala 2.8.1. According to comment by #incrop above, this is now fixed.
Related
I'm just in the process of upgrading from Scala 2.10.x to 2.11.2 and I'm receiving the following warning with the following code:
override def validateKey(key: String): Either[InvalidKeyError, Unit] =
keys.contains(key) match {
case true => Right()
case false => Left(InvalidKeyError(context, key))
}
Adaptation of argument list by inserting () has been deprecated: this
is unlikely to be what you want. signature: Right.apply[A, B](b: B):
scala.util.Right[A,B] given arguments: after adaptation:
Right((): Unit)
I am able to solve this by changing the "true" case statement to:
case true => Right(()) //() is a shortcut to a Unit instance
Is this the proper way to address this warning?
Edit: perhaps a "why we have to do this now" type answer would be appropriate, my cursory investigation seems to indicate that Scala inserting "Unit" when it thinks it needs to causes other problems
Automatic Unit inference has been deprecated in scala 2.11, and the reason behind this is that it can lead to confusing behavior, especially for people learning the language.
Here's an example
class Foo[T](value: T)
val x = new Foo
This should not compile, right? You are calling the constructor with no arguments, where one is required. Surprisingly, until scala 2.10.4 this compiles just fine, with no errors or warnings.
And that's because the compiler inferred a Unit argument, so it actually replaced your code with
val x = new Foo[Unit](()) // Foo[Unit]
As the newly introduced warning message says, this is unlikely to be what you want.
Another famous example is this
scala> List(1,2,3).toSet()
// res1: Boolean = false
calling toSet() should be a compile-time error, since toSet does not take arguments, but the compiler desperately tries to make it compile, ultimately interpreting the code as
scala> List(1,2,3).toSet.apply(())
which means: test whether () belongs to the set. Since it's not the case, you get a false!
So, starting from scala 2.11, you have to be explicit if you want to pass () (aka Unit) as an argument. That's why you have to write:
Right(())
instead of
Right()
examples taken from Simplifying Scala — The Past, Present and Future by Simon Ochsenreither.
Perhaps it should be Right(()). Have you tried that?
My explanation is that since the Right.apply is polymorphic it can take all kind of parameters, doing Right() means passing in a Unit and the compiler simply advise you that maybe that's not what you want, he doesn't know that this is what you actually want.
If you see your deprecate message it states:
... after adaptation: Right((): Unit)
Which means that the compiler has automatically decided that you are passing in a Unit, since this is kinda like void he doesn't really likes it, specifically passing in a Unit like () explicitly tells the compiler that you do want a Unit there. Anyway seems a new deprecation form scala 2.11, I can't reproduce this on 2.10.4.
In Scala REPL I can use Seq[String]() as a default value for a parameter of type Seq[T].
Welcome to Scala version 2.11.7 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_101).
Type in expressions to have them evaluated.
Type :help for more information.
scala> def d[T](foo: Seq[T] = Seq[String]()) = 12
d: [T](foo: Seq[T])Int
scala> d()
res0: Int = 12
Trying the same in IDEA, it complains “Seq[String] doesn't conform to expected type Seq[T]”. Why?
IntelliJ IDEA 2016.2.4
Scala Plugin 2016.2.1
Scala 2.11.7
Note 1: Sorry, I know that my example function does not make much sense. However, my real (and useful) function is unnecessarily complex to post it here.
Note 2: At first, instead of type T my type name in the example was Any which was not a good idea (because it shadowed scala.Any) and caused some confusion. Thus I fixed that.
When you say def d[Any], the Any here is a generic place holder. It does not point to class Any in scala. It basically shadows the Any class defined globally in scala. So, when you assign Seq[String] to Seq[Any], the compiler does not know any relation between String and Any. Note that Any could be replaced with any character / word as generic place holder. The result would be the same.
Now coming, to why this works in REPL, I am not exactly sure why REPL accepts if Seq[String] when given as a default value, but I was able to reproduce the error in repl when I do the same operation inside the method body.
The following code in REPL throws error:
def d[Any](foo: Seq[Any]) = {
val a: Seq[Any] = Seq[String]()
}
<console>:12: error: type mismatch;
found : Seq[String]
required: Seq[Any]
val a: Seq[Any] = Seq[String]()
^
I am not sure why REPL was not able to catch the error while given as a default argument.
One alternative theory is, in general when you use generics, the value of the type will be determined based on the caller. For example,
def d[A](a:A) = {}
d(1) // Type of A is Int
d("a") // Type of A is String
So, when you give default value, it assigns the value of String to Any. hence the compilation success.Intellij's Type Checker works based on the first theory and shows an error. But strangely as someone pointed out earlier, the compilation succeeds.
val x: AnyRef = 42
type mismatch: found Int(42) required: AnyRef
Note: an implicit exists from scala.Int => java.lang.Integer, but methods inherited from Object are rendered ambiguous. This is to avoid a blanket implicit which would convert any scala.Int to any AnyRef.
You may wish to use a type ascription: x: java.lang.Integer
I don't understand the emphasized part. What methods are rendered ambiguous, and how come? Are methods inherited from Object always "rendered ambiguous" in Scala? Is this a special scenario where methods somehow wind up multiple times in a type? I just don't see where the ambiguity comes from.
Scala has to pretend that Int is in a different place in the inheritance hierarchy than Java places it. This results in some awkwardness.
Consider the method notify. Waiting on an Int doesn't work--it's a primitive. Waiting on a newly-boxed java.lang.Integer doesn't work either, since the other threads may have ended up with their own separately-boxed Integers. You just don't want notify to work on an Int--it's the wrong thing to do.
But if you have the conversion Int => java.lang.Integer without anything unusual, you will be able to call notify on your Int.
To prevent this sort of usually-wrong behavior, the mechanism for failing to resolve a conversion due to ambiguity is hijacked. This keeps Int => java.lang.Integer serving to convert Int where AnyRef is expected (which would break the inheritance hierarchy), and prevents things like 42.notifyAll from working since even though java.lang.Integer has that method, the inferencing machinery is kept from noticing it.
The message you see is supposed to cover both the 42.notify case than the 42: AnyRef case.
Consider the following:
trait Foo { def bar: Any }
Now, 3 and X don't even have a method bar yet this compiles:
val a = 3.asInstanceOf[Int with Foo]
object X
val b = X.asInstanceOf[X.type with Foo]
This code, when compiled and run (on Scala 2.10.3), produces no error. However, the following will compile cleanly but will produce a ClassCastException at runtime:
val a1 = a.asInstanceOf[Foo]
val b1 = b.asInstanceOf[Foo]
How can this be? I can almost accept the fact that no static analysis/checking is performed on explicit casts, and they're discouraged and usually unneeded anyway—although I do think Scala could emit a warning about a definitely incorrect type cast given that there is no way object X can ever have the trait Foo, nor an integer—however I can't easily accept the fact that an integer can be cast at runtime to an intersection type containing a completely irrelevant and non-empty trait.
The problem is that JVM doesn't support intersection types, so there is no obvious runtime equivalent to asInstanceOf[Int with Foo]. This is basically the same issue as asInstanceOf[T], where T is a generic parameter (both in Scala and equivalent Java): you also have the cast which appears to succeed at runtime but actually inserts casts in later code which uses methods of T.
One possibility would be to compile the cast to Int with Foo as two casts to Int and to Foo, but this doesn't quite work in general (e.g. when there is no runtime cast to Foo itself, or when the value is passed to methods which take Int with Foo).
Here are two REPL sessions (inspired by this question, although my question is different):
Welcome to Scala version 2.9.2 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0).
Type in expressions to have them evaluated.
Type :help for more information.
scala> def ignore(it: String) = 42
ignore: (it: String)Int
scala> ignore(null.asInstanceOf[Nothing])
res0: Int = 42
And:
Welcome to Scala version 2.10.0 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0).
Type in expressions to have them evaluated.
Type :help for more information.
scala> def ignore(it: String) = 42
ignore: (it: String)Int
scala> ignore(null.asInstanceOf[Nothing])
java.lang.NullPointerException
at .<init>(<console>:9)
at .<clinit>(<console>)
at .<init>(<console>:7)
at .<clinit>(<console>)
at $print(<console>)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
...
The only difference is that the first is Scala 2.9.2 and the second is 2.10.0.
Can someone point to the changes in 2.10 that lead to this new behavior?
I know that casting to Nothing is a silly thing to do, and that the answer might be "this is all undefined behavior so just stop doing that", but it looks like the kind of thing that could potentially have implications for upgraders, and I don't remember running into any discussions of changes that would explain this.
Since Scala treats null differently from the None case on an option, even a null value of Nothing is problematic--there should be exactly zero instances of Nothing, not one instance that may or may not break depending on how you use it.
Thus, I cannot see how the old behavior is anything but a bug. It should be mentined in the release notes that it was fixed, but relying on .asInstanceOf[Nothing] to do anything save throw an exception is sufficiently contrary to type-sanity that I don't think anything more is needed. (In fact, I don't even think the release note is needed.)
This looks like an issue just with the console, not with the language. If you run this small application which invokes the exact same method, scala 2.10 doesn't have a problem with it.
object Test extends App {
override def main(args: Array[String]) {
println(takesString(null.asInstanceOf[Nothing]))
}
def takesString(a: String) = 42
}
To simplify your example from above, you could just type
null.asInstanceOf[Nothing]
and the console would give you the same error. I presume it has something to do with printing out the type.
Update: Looks like I accidentally ran against 2.9.2. Still fails as a script in 2.10 RC5 as the author points out in comment.
I know you are not expecting the answer "this is all undefined behavior, so (...)", but when you add "thing that could potentially have implications for upgraders", I must remember (even if it's obvious) that people can't rely or expect anything of the result of a thing that has undefined behavior, by its own definition.
In the specific case you mentioned, I don't think it is undefined behavior: it should throw an exception. Nothing is a subclass of Null, not the other way around - my first expectation, without testing, was that the line null.asInstanceOf[Nothing] would throw a ClassCastException, as null is not a Nothing. However, you can see that null is a special instance (as it is in Java). Try running:
scala> "aaa".asInstanceOf[Nothing]
java.lang.ClassCastException: java.lang.String cannot be cast to scala.runtime.N
othing$
at .<init>(<console>:8)
at .<clinit>(<console>)
My guess is that it happens because internally, obj.asInstanceOf[T] calls obj.getClass() in order to check the cast at runtime. As calling any method on null throws a NullPointerException, that exception is thrown before ClassCastException.
Back to your specific question, it seems that Scala 2.9.2 handles in a (very) special way that specific case. Running a few more tests:
scala> ignore(3.asInstanceOf[String])
java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.Stri
ng
at .<init>(<console>:9)
at .<clinit>(<console>)
scala> ignore({ println("test"); "aaa" })
test
res6: Int = 42
You can see that the argument is always being evaluated, save for your case. Scala 2.10 definitely has the most consistent behavior. However, this issue should not affect any developer upgrading to Scala 2.10; I can't see any case where obj.asInstanceOf[Nothing] is correct code.