Why does a + operation on two members of a template class cause a type mismatch error? - scala

lately I've decided to port my project's codebase to Scala for performance reasons but just as I was getting started, I was stopped by an error I did not understand. This is the minimal amount of code that causes the error:
class Foo[A](var x: A) {
def +(other: Foo[A]) = new Foo[A](this.x + other.x)
}
And the error itself:
test.scala:2: error: type mismatch;
found : A
required: String
def +(other: Foo[A]) = new Foo[A](this.x + other.x)
^
After looking around, I found some forum posts about similar errors which were apparently caused by Scala implicitly converting the template type to a string(?).

As mentioned in the comments, the compiler doesn't have enough information about type A to know how to + two of them.
The reason for the confusing error message is that the compiler "knows" that everything has a toString() method and that type String has a + method. So why doesn't it convert both to type String and + them together? It's because the A-to-String transition is an implicit conversion and the compiler won't do more than one implicit conversion in order to resolve an expression.
Thus the found:A, required:String error. The compiler is saying, "I've already converted the first A to String in order to resolve the + method but, now that I've done that, I can't do it again on the 2nd A element."
There are a few different ways around this. Here's one.
class Foo[A:Numeric](var x: A) {
def +(other: Foo[A]) =
new Foo[A](implicitly[Numeric[A]].plus(this.x, other.x))
}
A is restricted to types found in the Numeric type class. To add two As together, pull the implementation for Numeric[A] out of the implicit scope and invoke its plus() method.

The problem here is that the plus method in this.x + other.x is not the plus method defined in Foo[A]. It comes from A. And as A is still undefined, can be Any. The compiler, as always, will look a way to make things compile and in this case will find a conversion that will allow this.x call to + method. It will find this in Predef.scala that is in scope and has
implicit final class any2stringadd[A](private val self: A) extends AnyVal {
def +(other: String): String = String.valueOf(self) + other
}
Therefore if this.x is a String to be able to concatenate that.x, it should also be a String. Which is not the case.
You can check it in https://github.com/scala/scala/blob/706ef1b291134a5e5bce2275df2c222261f73451/src/library/scala/Predef.scala#L381

Related

Generic return type in Scala structural type method [duplicate]

I have the following snippet that (I think) defines a method addNumber1(x:T):T on a generic type T which is a subtype of AnyVal and has a method +(s:Int):T.
def addNumber1[T <: AnyVal {def +(s:Int):T}](x:T):T = {x + 1}
addNumber1(31) // compiles but throws exception
java.lang.NoSuchMethodException: java.lang.Integer.$plus(int)
at java.lang.Class.getMethod(Class.java:1786)
at .reflMethod$Method1(<console>:8)
at .addNumber1(<console>:8)
... 33 elided
I tried adding import scala.language.reflectiveCalls to suppress a feature warning but still get the error.
I am able to use this when working with AnyRef or Any as below:
def addNumber1[T <: Any {def +(s:Int):T}](x:T):T = {x + 1}
class Foo(s:String) {def +(i:Int) = new Foo((s+1).toString)} // random code
class Bar(s:Foo) {def +(i:Int) = new Bar(new Foo(i.toString))} // random code
addNumber1(new Foo("1")) // works
addNumber1(new Bar(new Foo("1"))) // works
addNumber1(1) // compiles but gives exception
You run into an intersection of quite a few features:
So far as the initial stages of Scala compiler are concerned (including typechecking), Int does have an (overloaded) + method. But this "method" is treated specially by the later stages (as are all methods on Int, because it isn't really a class).
Methods called + and defined in Scala are translated to methods called $plus in bytecode, since + is not a legal identifier there. Since + on Int is special, as mentioned above, this doesn't apply to it. Since the structural types are implemented using Java reflection, your addNumber1 looks somewhat like
def addNumber1(x: Object) = x.getClass.getMethod("$plus").invoke(x, 1)
To call addNumber1 on an int, it has to be boxed to Integer first, because int is not an object. Integer, not being a Scala type, doesn't have a $plus method. In Scala you can write something like val x: Integer = ...; x + 1, but this uses an implicit conversion which Java reflection has no idea about.
I think the problem has nothing to do with AnyVal, AnyRef Or Any.
addNumber1(new Foo("1"))
This works because you indeed defined a Foo class that provides an implementation of def +(s:Int):T.
addNumber1(1)
This doesn't work because Integer class doesn't provide it, as is mentioned in the exception:
java.lang.NoSuchMethodException: java.lang.Integer.$plus(int)

Implicit conversions weirdness

I am trying to understand why exactly an implicit conversion is working in one case, but not in the other.
Here is an example:
case class Wrapper[T](wrapped: T)
trait Wrapping { implicit def wrapIt[T](x: Option[T]) = x.map(Wrapper(_))
class NotWorking extends Wrapping { def foo: Option[Wrapper[String]] = Some("foo") }
class Working extends Wrapping {
def foo: Option[Wrapper[String]] = {
val why = Some("foo")
why
}
}
Basically, I have an implicit conversion from Option[T] to Option[Wrapper[T]], and am trying to define a function, that returns an optional string, that gets implicitly wrapped.
The question is why, when I try to return Option[String] directly (NotWorking above), I get an error (found : String("foo") required: Wrapper[String]), that goes away if I assign the result to a val before returning it.
What gives?
I don't know if this is intended or would be considered a bug, but here is what I think is happening.
In def foo: Option[Wrapper[String]] = Some("foo") the compiler will set the expected type of the argument provided to Some( ) as Wrapper[String]. Then it sees that you provided a String which it is not what is expected, so it looks for an implicit conversion String => Wrapper[String], can't find one, and fails.
Why does it need that expected type stuff, and doesn't just type Some("foo") as Some[String] and afterwards try to find a conversion?
Because scalac wants to be able to typecheck the following code:
case class Invariant[T](t: T)
val a: Invariant[Any] = Invariant("s")
In order for this code to work, the compiler can't just type Invariant("s") as Invariant[String] because then compilation will fail as Invariant[String] is not a subtype of Invariant[Any]. The compiler needs to set the expected type of "s" to Any so that it can see that "s" is an instance of Any before it's too late.
In order for both this code and your code to work out correctly, I think the compiler would need some kind of backtracking logic which it doesn't seem to have, perhaps for good reasons.
The reason that your Working code does work, is that this kind of type inference does not span multiple lines. Analogously val a: Invariant[Any] = {val why = Invariant("s"); why} does not compile.

Any class in scala

I am beginner in scala language, I am confused with Any class in scala.
def f(x: Any) = println(x)
is above code represents that x variable can be of any datatype(example:int,string etc.)
rewritten code:
def f(x:Any)=x+5
<console>:13: error: type mismatch;
found : Int(5)
required: String
def f(x:Any)=x+5
if x can accept any type then why am I getting above error. I might confused understanding of any in scala. Please correct me.
In a statically typed language you can only call a method m on a value x of type A if m is defined by A. By the nature of Any, there aren't any useful methods on Any that you could call (except a few things like toString or hashCode), certainly no plus operation is defined. Imagine you passed a Boolean into that method, which is allowed since Boolean is a sub-type of Any. If the compiler allowed your code, it would run into a problem, because there is no such thing as + on a Boolean. In a dynamically typed language you could run that code and would then encounter a runtime error.
The error message looks weird, because you can concatenate strings with + and due to an implicit conversion it is possible to concatenate things with strings:
def f(x: Any) = x + "hello" // implicitly converts x to a string
f(true) // "truehello"
This is a source of great confusion and hopefully will disappear from the language. If you used a different method, the error would be more obvious:
def f(x:Any)=x-5
<console>:54: error: value - is not a member of Any
def f(x:Any)=x-5
^
If you look at println() sources you will see:
public void println(Object x) {
String s = String.valueOf(x);
synchronized (this) {
print(s);
newLine();
}
}
where String.valueOf is
public static String valueOf(Object obj) {
return (obj == null) ? "null" : obj.toString();
}
Class Any already has toString method but doesn't have + method (you know that all operators such +, - etc. are methods, see http://tomjefferys.blogspot.com/2011/11/operator-overloading-in-scala.html).

Implicit conversion paradox

If I try to define an implicit conversion for a primitive type, then it doesn't seem to work. E.g.:
implicit def globalIntToString(a: Int) : String = { a.toString() + "globalhi" }
1.toInt + "hi"
The above will still return simply "1hi" as the result.
However, it seems that if I parameterize a class or a def and then pass in the implicit for the parametrized case, then it seems to work. Does anyone know what the reasons are? E.g. does this have something to do with boxing/unboxing of primtives (e.g., parameterized primitives are boxed)? Does implicit only work with reference types and not primitive types?
class typeConv[T] { implicit def tToStr(a: T) : String = { a.toString() + "hi" } }
class t[K](a: K)(tc : typeConv[K]) { import tc._; println(a + "cool"); println(1.toInt + "cool" ) }
new t(1)(new typeConv[Int])
There are a few subtle things going on here. #yan has already explained the core issue - I'll try to add some more specific information.
As noted, 1.toInt + "hi" will never use any implicit conversion because Scala Int class actually has a method + that takes a String parameter. The compiler will look for an implicit view only when it can't find matching member in the original type.
A little more complicated stuff is happening inside your t class. Scalac will look for implicit conversion from a generic type K to any type that has a + method that takes a String parameter. There will be two candidates for such a conversion: your own tc.tToStr and Scala built-in scala.Predef.any2stringadd.
Normally, any2stringadd would be used, but in your example, your own conversion is used. Why does it have precedence over any2stringadd?
During implicit search, tc.tToStr is seen as a function of type K => String, while any2stringadd is seen as a function of type Any => StringAdd. My guess is that K => String is seen by the compiler as a more specific conversion than Any => StringAdd, but someone would have to confirm it with a proper reference to Scala Language Specification.
As you can see, defining such conversions may cause you a lot of strange behaviour. I'd definitely say that introducing an implicit conversion to a String is asking for trouble.
This happens because Scala defines a + operator on the Int type that takes a String and does not need to resolve an implicit conversion. Also, converting to String is usually a bad idea, as you'd generally have a custom, one-off type that would define the methods you're trying to add.

Scala - how to go resolve "Value is not a member of Nothing" error

This example code is based on Atmosphere classes, but if someone could give me some insights into what the error means in general, I think I can figure out any Atmosphere-specific solution...
val bc = BroadcasterFactory.getDefault().lookup(_broadcasterId)
bc.broadcast(message)
After the first line, bc should contain a handle to an object whose class definition includes the method broadcast() -- in fact, it contains several overloaded variations. However, the compiler chokes on the second line of code with the following: "value broadcast is not a member of Nothing"
Any ideas/suggestions on what would be causing this?
Thanks.
EDIT: signature for [BroadcasterFactor].lookup :
abstract Broadcaster lookup(Object id)
Note: 1) that is the signature version that I've used in the example, 2) it is the java Inteface signature - whereas the getDefault() hands back an instantiated object that implements that interface.
Solution: force type cast on value:
val bc: Broadcaster = BroadcasterFactory.getDefault().lookup(_broadcasterId)
Nothing is the type name. It's the subtype of all other types. You can't call methods from Nothing itself, you have to specify exact type ((bc: ExactType).broadcast(message)). Nothing has no instances. Method, that returns Nothing will, actually, never return value. It will throw an exception eventually.
Type inference
Definition of lookup:
abstract public <T extends Broadcaster> T lookup(Object id);
in scala this definition looks this way:
def lookup[T <: Broadcaster](Object id): T
There is not specified type parameter in lookup method. In this case compiler will infer this type parameter as the most specific type - Nothing:
scala> def test[T](i: Int): T = ???
test: [T](i: Int)T
scala> lazy val x = test(1)
x: Nothing = <lazy>
scala> lazy val x = test[String](1)
x: String = <lazy>
You could specify type parameter like this:
val bc = BroadcasterFactory.getDefault().lookup[Broadcaster](_broadcasterId)
Draft implementation
In development process lookup can be "implemented" like this:
def lookup(...) = ???
??? returns Nothing.
You should specify either result type of lookup method like this: def lookup(...): <TypeHere> = ... or type of bc: val bc: <TypeHere> =.