The theoretically-inclined may judge if the subject correctly characterizes the situation I am about to describe.
At some point while programming a library (in Scala, of course), I thought it would be a nice idea to abstract over some compiler-powered magic regarding the handling of for-comprehensions, and in particular those compiled to foreach() calls. So, I came up with this:
type ForEach[T] = {
def foreach(f: T => Unit): Unit
}
Now, let’s define a generic function:
def foreachWrapper[T](container: ForEach[T], f: T => Unit) {
for(value < - container)
f(value)
}
So, any type adhering to the ForEach structural contract can be passed to foreachWrapper. Of course this is a trivial function, but suffices for the purpose of this post.
Now, we know that Lists and Arrays are specially handled by the compiler when appearing in a for(v <- c) construct, and we deserve the right to believe that this:
foreachWrapper(List(1, 2))
and this:
foreachWrapper(Array(1, 2))
will type-check. And indeed they do, as expected. So our program is correct.
But if we try to run it (see the complete code at the bottom of this post), we get an exception:
java.lang.NoSuchMethodException: [I.foreach(scala.Function1)
Hmmmm.... What the Hack??? The situation is blurred (note that [I is the class of an integer array in Java). But wait a minute!
We know that structural types, implementation-wise, interfere with reflection, and there is where the exception comes from. And this is all because Scala does its best to handle arrays in the most balanced way.. Array(1, 2) is compiled to a respective Java array but Java arrays do not define a foreach method!
As expected, If we force Scala to box the array, using this trick:
def boxMe[T](me: Array[T]): Array[T] = me
then there is no problem at all…
C’est la vie. You get something, you lose something else…
The complete code follows:
object foreach {
type ForEach[T] = {
def foreach(f: T => Unit): Unit
}
def foreachWrapper[T](container: ForEach[T], f: T => Unit) {
for(value <- container)
f(value)
}
def boxMe[T](me: Array[T]): Array[T] = me
def rollIt[T](container: ForEach[T]) {
println("-- Iterating over: " + container.getClass.getName)
foreachWrapper(container, println)
}
def main(args: Array[String]) {
val intList = List(1, 2)
val intArray = Array(1, 2)
rollIt(intList)
rollIt(boxMe(intArray))
rollIt(intArray) // Exception!
}
}