I’ve written previously about Scala ; today I’m going to write about a potential ‘gotcha’ of Scala’s type inferencing. Type inferencing means that instead of writing int x = 100; as we would in Java, you can instead write val x = 100

and the compiler is smart enough to figure out that you mean an integer. Let’s look at one case in which the use of implicit typing came back to bite me.

The setup

I was writing a routine that calculates the ‘average’ color of an image (where we define the average color as the color formed by the average red, blue, and green components of all the pixels in the image). A first pass might look like this:

def calculateAverageColor(image:BufferedImage):Color = { var redSum = 0 var greenSum = 0 var blueSum = 0 // calculate the sum of each channel here val red = (redSum / numPixels) val green = (greenSum / numPixels) val blue = (blueSum / numPixels) new Color(red, green, blue) }

(I’m leaving off the actual summation algorithm, as it’s not important to illustrate how the problem manifests itself.)

This code is almost right, but it has a problem. Can you guess what it is?

Problem #1

In a lot of cases this code will work fine. But if you have an extremely high resolution image, or you have an overexposed image (where many of the pixels are near white, implying a high red, green, and blue value), any of the individual sums can overflow , if the sum exceeds the max value that an integer can hold. Due to the way binary numbers are implemented ( “Twos complement” ), this results in a negative number.

scala> java.lang.Integer.MAX_VALUE res0: Int = 2147483647 scala> java.lang.Integer.MAX_VALUE+1 res2: Int = -2147483648

If this happens, then we end up trying to construct a color with negative red, green, or blue values; this will result in an IllegalArgumentException.

The easiest way to fix this is to use a bigger datatype to store the running sum; let’s use a Long instead. This allows us a maximum value of 9223372036854775807; that should be plenty big to store whatever running total we have.

def calculateAverageColor(image:BufferedImage):Color = { // Declare the sums as longs so we don't have to worry about overflow var redSum:Long = 0 var greenSum:Long = 0 var blueSum:Long = 0 // calculate the sum of each channel val red = (redSum / numPixels) val green = (greenSum / numPixels) val blue = (blueSum / numPixels) new Color(red, green, blue) }

This code looks OK at a cursory glance; if you actual use it, you will quickly get an exception:

java.lang.IllegalArgumentException: Color parameter outside of expected range: Red, Green, Blue

Problem #2

At this point, you might start debugging the process by printing out the values of red, green, and blue. Sure enough they’ll be in the range [0, 255], just as you need for the Color constructor. What is going on?

There are two related problems. The first is that the type of red, green, and blue are not integers, due to the Long value in the computation. The compiler sees the Long and (correctly) infers that the type of red, green, and blue must be Long.

This wouldn’t be a problem, except that there is no Color constructor that takes in Long arguments. Yet the code compiles fine. Why?

Well, it’s because of the implicit widening conversions present in the Java language. These (usually) make our lives as programmers much easier. If a method is defined to take in an integer and we pass it a short instead, the short is implicitly converted to an integer with no extra effort on our part. This is done automatically because there is no danger of truncation; because the type that is implicitly converted to is larger (wider), it can always hold the value of the smaller type.

According to the Sun specification

”

The following 19 specific conversions on primitive types are called the widening primitive conversions:

byte to short , int , long , float , or double

to , , , , or short to int , long , float , or double

to , , , or char to int , long , float , or double

to , , , or int to long , float , or double

to , , or long to float or double

to or float to double

”

The implicit conversion that tripped me up was that longs are implicitly promoted to float. The reason the method compiled, even though there is no constructor that takes long r,g,b arguments, is because there is a constructor that takes float arguments, and the longs were automatically converted to floats! See the Color documentation for more. Unlike the integer constructor, which takes RGB values in the range [0,255], the float constructor takes RGB values in the range [0,1].

The solution is to convert the longs back into ints, via a cast. In Java this would be accomplished with

long redSum = ...; int averageRed = (int) (redSum/numPixels);

In Scala you instead must do

val redSum:Long = ... val averageRed:Int = (redSum/numPixels).asInstanceOf[Int]

The fixed code as a whole thus looks like

var redSum:Long = 0 var greenSum:Long = 0 var blueSum:Long = 0 // calculate the sum of each channel val red:Int = (redSum / numPixels).asInstanceOf[Int] val green:Int = (greenSum / numPixels).asInstanceOf[Int] val blue:Int = (blueSum / numPixels).asInstanceOf[Int] new Color(red, green, blue)

Conclusion

One of the nice things about Scala is that you do not need to explicitly declare the types of your variables. In one sequence of unfortunate events, the variables that looked like ints were in fact longs, leading to an implicit conversion to the float primitive type, which in turn caused the incorrect constructor to be invoked, and an IllegalArgumentException. Hopefully you can avoid doing something so foolish as a result of reading this post.