In the following example, f3 can take a Iterable[Array[Int]]
def f3(o:Iterable[Iterable[Any]]):Unit = {}
f3(Iterable(Array(123))) // OK. Takes Iterable[Array[Int]]
but if I assign the Iterable[Array[Int]] to a local variable, it cannot:
val v3 = Iterable(Array(123))
f3(v3) // Fails to take Takes Iterable[Array[Int]]
with the error:
Error:(918, 10) type mismatch;
found : Iterable[Array[Int]]
required: Iterable[Iterable[Any]]
f3(x)
What the fudge? Why does the first example work, but not the seconds. It seems to have something to do with nested generics:
def f(o:Iterable[Any]):Unit = {}
f( Array(123))
val v1 = Array(123)
f(v1) // OK
def f2(o:Iterable[Any]):Unit = {}
f2( Iterable(Array(123)))
val v2 = Array(123)
f(v2) // OK
With scala.2.11
The UnboundLocalError: local variable referenced before assignment error is raised when you try to assign a value to a local variable before it has been declared. You can solve this error by ensuring that a local variable is declared before you assign it a value.
The Python "UnboundLocalError: Local variable referenced before assignment" occurs when we reference a local variable before assigning a value to it in a function. To solve the error, mark the variable as global in the function definition, e.g. global my_var .
Variable Assignment To "assign" a variable means to symbolically associate a specific piece of information with a name. Any operations that are applied to this "name" (or variable) must hold true for any possible values.
Assignment expressions allow you to assign and return a value in the same expression. For example, if you want to assign to a variable and print its value, then you typically do something like this: >>> >>> walrus = False >>> print(walrus) False.
First of all, it's important that Array
doesn't extend Iterable
(because it's a Java type). Instead there is an implicit conversion from Array[A]
to Iterable[A]
, so expected types matter.
In the first case: Iterable(Array(123))
is an argument to f3
and thus is typechecked with expected type Iterable[Iterable[Any]]
. So Array(123)
is typechecked with expected type Iterable[Any]
. Well, its actual type is Array[Int]
and the compiler inserts the conversion (because Iterable[Int]
conforms to Iterable[Any]
). So this is actually Iterable(array2iterable(Array(123))
(I don't remember the exact name).
In the second case f3
has the type Iterable[Array[Int]]
: there is nothing to trigger the implicit conversion in the val f3 = ...
line, right? And there is no implicit conversion from Iterable[Array[Int]]
to Iterable[Iterable[Int]]
(or, more generally from Iterable[A]
to Iterable[B]
when there is an implicit conversion from A
to B
), so the next line fails to compile. You could write this conversion yourself, but it wouldn't help e.g. to convert Array[Array[Int]]
to Iterable[Iterable[Int]]
.
And of course, if you use Iterable[Any]
, there is again nothing to trigger the implicit conversion!
This has to do with the way type inference/unification works in Scala.
When you define a variable and leave out the type, Scala applies the most specific type possible:
scala> val v1 = Iterable(Array(123))
v1: Iterable[Array[Int]] = List(Array(123))
However, when you specify the expected type (eg by passing the value to a function with a defined parameter type) Scala unifies the given parameter with the expected type (if possible) :
scala> val v2 : Iterable[Iterable[Any]] = Iterable(Array(123))
v2: Iterable[Iterable[Any]] = List(WrappedArray(123))
Since Int
is a subtype of Any
, unification occurs and the code runs just fine.
If you want your function to accept anything that is a subtype of Any
(without Scala's unification help) you will have to define this behavior explicitly.
Edit:
While what I am saying is partially true, see @AlexyRomanov's answer for a more correct assessment. It seems that the "unification" between Array
and Iterable
is really an implicit conversion being called when you pass Iterable(Array(123))
as a parameter (see the effect of this in my declaration of v2
).
Suppose you have a bit of code where the compiler expects type B
but finds type A
instead. Before throwing an error, the compiler checks a collection of implicit conversion functions for one with the type A => B
. If the compiler finds a satisfactory conversion, the conversion is applied automatically (and silently).
The reason f3
doesn't like v1
is because it is too late to call an implicit conversion on the inner Array[Int]
and no existing implicit conversion exists for Iterable[Array[Int]] => Iterable[Iterable[Int]]
, though it would be trivial to implement, as I show below:
scala> implicit def ItAr2ItIt[T](ItAr: Iterable[Array[T]]): Iterable[Iterable[T]] = ItAr.map(_.toIterable)
ItAr2ItIt: [T](ItAr: Iterable[Array[T]])Iterable[Iterable[T]]
scala> def f3(o:Iterable[Iterable[Any]]):Unit = println("I like what I see!")
f3: (o: Iterable[Iterable[Any]])Unit
scala> val v3 = Iterable(Array(123))
v3: Iterable[Array[Int]] = List(Array(123))
scala> f3(v3)
I like what I see!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With