Aniruddha Sinha Aniruddha Sinha - 1 month ago 6x
Scala Question

Subtyping leads to Any: Bug in compiler or issue with my code?

Let me get straight into the problem that I faced while hanging around with type bounds.

Let's consider the following...
I created a function 'foo' like this

def foo[A,B](x:A,y:B):(A,B)=(x,y)

I invoked foo in scala worksheet, like


I obtained a result like

res0: (String, Int) = (Mars,2400)

Notice the inferred types of Mars and 2400

Now I wanted to enforce that the function 'foo' accepts Integers or floats or Doubles (any type that is a subtype of AnyVal).

To enforce I wrote a code like

def fooNew[A<:B,B](x:A,y:B):(A,B)=(x,y)

The inferred types from the previous code was (String,Int) and when I invoked fooNew like


I was surprised to see that the compiler did let my code pass and did not raise the error instead it did give an output like

res0: (String, Any) = (Saturn,2400)

Now, the desired way of enforcing did not work here. Had I done something like this

def fooNew[A<:B,B<:AnyVal](x:A,y:B):(A,B)=(x,y)

The compiler would have surely raised an error for me and it did!

Error:(2, 2) inferred type arguments [String,Any] do not conform to method fooNew's type parameter bounds [A <: B,B <: AnyVal]

I want to ask, why didn't the compiler the type as Int instead it inferred the type Any and let my code pass the type checks?
Do I always need to enforce the second type to be a subtype of AnyVal instead of letting the compiler infer it for me? or is it a bug in the compiler.
Seek pardon if you found my question misleading or not upto your expectations.

Currently I am using scala-library 2.11.8



You can think of using your original declaration with inferred types as "find A and B such that x has type A, y has type B, and A is a subtype of B". Since A = String and B = Any satisfy these conditions, the compiler correctly infers them (there are also other solutions, e.g. A = B = Any, but this one is the most specific).

But you can change the declaration to tell the compiler "find A and B such that x has type A and y has type B, and then check that A is a subtype of B". This is done as follows:

def fooNew[A,B](x:A,y:B)(implicit evidence: A <:< B): (A,B)=(x,y)

This works because the compiler will only use the first parameter list to infer A and B. Search for "generalized type constraints" to find more information about <:< and =:=.