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.
Related
I used that kind of line to test my implicits making it implicit by copy-paste accident. It took me quite some time to figure out, why this compiles despite me not expecting it to compile:
> console
[info] Starting scala interpreter...
[info]
Welcome to Scala version 2.11.7 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_66).
... skipped some comment lines ...
scala> case object Foo
defined object Foo
scala> object Bar { implicit val f: Foo.type = implicitly[Foo.type] }
defined object Bar
scala> val x = Bar.f
x: Foo.type = null
scala>
I would expect Bar to fail compilation, because there is NO implicit val of type Foo.type (the case object is NOT declared implicit).
For me it looks like the compiler uses f's own declaration (left hand side) to complete its implementation (right hand side).
Is this really the intended behavior? At runtime this results in unexpected behavior with null values (mostly NPEs for me).
This is happening because f is declared as implicit. So in a way the right hand side of implicit val f: Foo.type = implicitly[Foo.type] resolves to the implicit val f itself !
If you remove the implicit from that line the compilation would fail.
What i am wondering is why are you using such a line though.
This is definitely a issue to me. Someone looks like already took a note last year though.
Enhancement Request
I'm new to Scala and I'm using the shapeless library to manipulate tuples. (Though this isn't specific to shapeless)
I've run in to a problem related to implicit parameters, which I don't quite understand. Here are there signatures:
def reverse(implicit reverse: Reverse[T]): reverse.Out = reverse(t)
def drop[N <: Nat](implicit drop: Drop[T, N]): drop.Out = drop(t)
And here is it in use:
val foo = ("foo","bar","other").reverse
val bar = ("foo","bar","other").drop(1)
However on Scala 2.10 I get this error:
Error:(25, 37) could not find implicit value for parameter reverse:
shapeless.ops.tuple.Reverse[(String, String, String)]
val zzy = ("foo","bar","other").reverse
Error:(29, 37) not enough arguments for method reverse: (implicit
reverse: shapeless.ops.tuple.Reverse[(String, String,
String)])reverse.Out. Unspecified value parameter reverse.
val foo = ("foo","bar","other").reverse
^
I'm not sure what the implicit parameter is that it is trying to reference, or why I need it. Also, this seems to work on 2.11 (but IntelliJ flags it)
On Scala 2.10.x you need to have the macro-paradise plugin included in your build: see the shapeless documentation here.
IntelliJ often reports spurious errors against code which uses aspects of the Scala type system which it doesn't have adequate support for. This is especially true of code which uses dependent method types as reverse and drop above do (notice that the result type of the methods depend on the argument values). In all cases the command line compiler is authoritative.
Can anyone tell me why this limitation exists ? Is it related to JVM or Scala compiler ?
$ scala
Welcome to Scala version 2.10.4 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_79).
Type in expressions to have them evaluated.
Type :help for more information.
scala> def toText(ints: Int*, strings: String*) = ints.mkString("") + strings.mkString("")
<console>:7: error: *-parameter must come last
def toText(ints: Int*, strings: String*) = ints.mkString("") + strings.mkString("")
A method in Scala can have multiple varargs if you use multiple parameter lists (curried syntax):
scala> def toText(ints: Int*)(strings: String*) =
ints.mkString("") + strings.mkString("")
scala> toText(1,2,3)("a", "b")
res0: String = 123ab
Update: Multiple varargs in a single parameter list would create a syntax problem - how would the compiler know which parameter a given argument belongs to (where does one list of arguments end, and the next begin, especially if they are of compatible types?).
In theory, if the syntax of the language were modified so that one could distinguish the first and second argument lists (without currying), there's no reason this wouldn't work at the JVM level, because varargs are just compiled into an array anyway.
But I very much doubt that it's a common enough case to justify complicating the language further, especially when a solution already exists.
See also this related Java question and answer.
Scala is based on Java Virtual Machine, which is set to accept varargs only as the last argument, only one per method arguments set. No working this around, this is how the compiler works.
To put it into perspective, imagine a method signature like this:
someMethod(strings1: String*, strings2: String*)
Let's say you pass 4 separate Strings when calling it. The compiler would not know which String object belongs to which vararg.
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).
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.