Scala an invariant general type parameter ignored by the method parameter type depends on whether the argument is a literal expression against a variable

Summary

If I pass a literal expression as an argument to a function, this should not coincide with the first evaluation of the same expression of the literal expression, and then bind the variable to the value returned from that evaluation, and then pass the variable name as the same argument for the same function ? And if this literal expression returns the wrong type for the function parameter, then skipping the step of assigning the value to the intermediate variable, the type of which Scala deduces from the return value of the expression, should not skip this incompatible function type where the type-mismatch error occurred before it, right? But is this not shown in the following example?

Example

Here I am trying to get a parameter of a function like Array[Super] to accept the value of Array[Sub] . Declare the following in Scala repl. Pay attention to the type of function of one parameter:

 class Super class Sub extends Super def wantsSuperArray(a: Array[Super]) { println(a.size) } 

Then build an instance of Sub

 scala> val s = new Sub s: Sub = Sub@2c9fa2fb 

Create an Array[Sub] :

 scala> val subArray = Array(s) subArray: Array[Sub] = Array( Sub@2c9fa2fb ) 

It will be noted below that a generic Array is type-invariant and that Array[Sub] not Array[Super] although a Sub is Super :

 scala> wantsSuperArray(subArray) <console>:13: error: type mismatch; found : Array[Sub] required: Array[Super] Note: Sub <: Super, but class Array is invariant in type T. You may wish to investigate a wildcard type such as `_ <: Super`. (SLS 3.2.10) wantsSuperArray(subArray) ^ 

There are no surprises yet.

Amazing observation

We just saw that wantsSuperArray() will not be used as the argument type of a Array[Sub] . So why does the following not generate the same type mismatch error message as above?

 scala> wantsSuperArray(Array(new Sub)) 1 

Similarly, why does this not cause errors?

 scala> wantsSuperArray(Array(s)) 1 

Keep in mind that the compiler sequentially considers three options with the help of repl, namely it refuses to compile and produce the same type mismatch error for the first and compile the second and third.

Additional Information

If we explicitly parameterize Array as follows, then an error message appears:

 scala> wantsSuperArray(Array[Sub](new Sub)) <console>:11: error: type mismatch; found : Array[Sub] required: Array[Super] Note: Sub <: Super, but class Array is invariant in type T. You may wish to investigate a wildcard type such as `_ <: Super`. (SLS 3.2.10) wantsSuperArray(Array[Sub](new Sub)) ^ 

So it’s obvious that if there is no intermediate variable, Scala can see what type wantsSuperArray wants and does some kind of conversion, possibly dropping from Array[Sub] to Array[Super] . However, this is similar to getting it, because I still think that choosing to use or not use the intermediate variable should not cause such a difference in the program operation, and this particular situation seems to perform the casting instead of raising the error to the programmer would expect based on the immutability of a typical parameter of type Array .

Questions

If I believe that when calling wantsSuperArray() as defined above, passing a literal expression should be the same as passing a variable name containing the value obtained by evaluating the same expression as shown above, what am I misunderstanding?

  • How can I improve my understanding of what I am observing and complaining here?

  • Where in the Scala documentation can I read about the phenomenon that I am observing here to be able to understand this and never be surprised at it again?

+4
source share
2 answers

This is because scala performs an input operation of type type of the expected parameter type wantsSuperArray . Thus, although Array(new Sub) , taken alone, will be output as an expression of type Array[Sub] , the compiler sees that you are in a context where a value of type Array[Super] is expected, and thus when calling Array.apply (which is generic) it tries to use Super as a type parameter (instead of Sub ), which correctly enters ( Array.apply takes a list of vararg parameters of type T , here T = Super ), and you pass an instance of Sub , which is a subtype of Super , which is sound).


Here is the relevant excerpt from the scala specification, chapter 6.1: Typing Typing, chapter 6.6: Function applications and chapter 6.26.4: Local type inference (highlighting):

Writing expressions is often relative to some expected type (1) (which may be undefined). When we write "expression e is expected to match type T," we mean: the expected type of e is equal to T, and the type of expression e must match to T.

...

The application f (e1, ..., em) applies the function f to the expressions of the arguments e1, ..., em. If f has a method type (p1: T1, ..., pn: Tn) U, the type of each argument expression ei is entered with the corresponding parameter type Ti as the expected type (2) . Let Si be the type of the argument type ei (i in 1, ..., m). If f is a polymorphic method, the local type is used to determine the type arguments for f (3) .

...

If f is a polymorphic method, this is applicable if local type inference can determine type arguments so that the applicable method is applicable (4) * .

...

The local type outputs type arguments that are passed to expressions of the polymorphic type. Let's say e is of type [a1>: L1 <: U1, ..., an>: Ln <: Un] T and there is no explicit type, the parameters are given ... If the expression e appears as a value without applying value to the arguments, arguments of the type inferred by solving a constraint system that associates expressions of type T with the expected type of pt (5)

Points (3) and (4) explain how, in an Array(new Sub) expression, output the output stream from new Sub to Array.apply to get an Array[Sub] . This is a “simple” case, which, apparently, was not a problem. If you just accept this rule, Array(new Sub) should be entered as Array[Sub] . Indeed, this is what happens when it is entered in isolation (for example, in val subArray = Array(new Sub) , subArray is of type Array[Sub] ).

But points (1), (2) and (5) together also say that in wantsSuperArray(Array(new Sub)) expected type of the wantsSuperArray parameter (which is equal to Array[Super] ) is passed into the expression Array(new Sub) (because this is an expression of a polymorphic type, where the type parameter is not explicitly specified). Thus, the expression Array(new Sub) is evaluated as the expression Array[Super](new Sub) . In other words, it is typed as Array[Super] .

+7
source

Since you really cannot make Array(s) , since type information is required, even if it is Any , the compiler will run input type input code and find that it can create Array[Super] , and it will be suitable for calling method.

When you explicitly say that it is Array[Sub] , the compiler will not start type output because you have already given the type yourself, and since the type does not match the method, cause a compilation error.

0
source

All Articles