In this second installment of this series on the Scala programming language, Venkat shows how Scala’s static typing leads to low ceremony programming.
In this second part of the series we will look at the statically typed nature of Scala and its low ceremony.
Java programmers are used to static typing. Scala is also statically typed. However, it employs type inference to figure out types at compile time. So for the most part, you don’t have to specify type information. While Scala provides type inference, it does not go to extremes to determine the type (like F#, for example, which will analyze the method body to figure out types of parameters). Scala type inference reduces the number of keystrokes you have to type and reduces noise in code without compromising readability or understandability.
Let’s first look at how we’d define a variable, set its type, and assign it an initial value. Then we can look at what type inference buys us.
You use var to define a mutable variable, a variable whose value can change (true to its varying nature). When defining a variable, use a colon to separate the name of the variable from its type. Finally you can assign a value to the variable using the all-too-familiar equals operator.
Let’s extend that example to change the variable’s value and print the new value.
We initialized the variable greet to “hello” and then changed it to “howdy.” The output from the code is shown below:
At this point if you listen keenly you may hear Scala laughing at the redundancy in typing. Scala already knows that greet is of type String from the fact that its value was initialized to “hello.” So, the : String part is redundant, and you can omit it:
Scala infers the type of the variables at compile time. If Scala notices any ambiguity or errors, it will report right away and will not run any part of the code.
Let’s see what happens if we try to set a value of the greet variable to something not compatible with its type:
When you try to run this script, you will get an error:
Scala is quick to tell us that the value 1 we’re assigning to the variable does not match with the type String, the type which it inferred at the point of declaration.
Scala’s type inference plays well far beyond simple variable declarations, too. For instance, in the declaration
the type declaration is redundant and you can write it as:
I must acknowledge that this can be a blessing or a curse, depending on how you look at it. numbers is inferred as a List[Int] based on the values you present to the list. If all the values are of the same type, then this is not a concern. However, if you mix the values of different type, you will not get any error. For example:
will result in an error:
However, if you leave out the type in the declaration, as in:
you will not get any errors and the type of the list1 reference will be inferred as List[Any]. If you did not expect that and you mixed the type for the value in error, the type inference will not have alerted you and will not have acted in your favor. So use caution when relying on type inference.
Type inference is also convenient when defining function values. We will discuss function values later in this series, but for now think of them as anonymous code blocks. To iterate over a list of numbers you can pass a function value to the internal iterator:
With numbers defined as List(1, 2, 3), this code will print
as output. Scala is performing type checking to ensure that the type of the parameter e in the anonymous code block (function value) matches the type of the elements in the list numbers. If you declare a different type, you will get a clear compilation error, as in:
and the error reported by the compiler is:
Scala reports that it found a parameter of type Double when Int was required.
You can avoid this journey entirely by letting Scala infer the type for the parameter of the function value:
This code will also produce the output
however it is less noisy, since we let Scala infer the type. At this point Scala checks the signature of the function that the foreach function expects as parameter and infers the type of the parameter e to be the same. You will appreciate the benefit this conciseness offers even more when the anonymous code block has more parameters.
If you make a mistake and treat the parameter as some other type, Scala will give you an error at the point of usage. For example, suppose you mistook the parameter to be a String and decided to call the length method on it:
Scala will give you an error that clearly explains the problem as it sees it:
When defining anonymous code blocks or function values, you can safely leave out the type declaration. When you are defining functions or methods, however, you’ll find that Scala requires you to specify the type. Where possible, I prefer to provide meaningful variable names and let the context tell me what the type is, allowing Scala to infer the type. Coming from Java you may find that this takes a bit of getting used to, but once you get a handle on it, you’ll find the reduced noise quite refreshing.
We saw how specifying the type is optional as Scala infers the type for us wherever possible. Quite a few more things are optional in Scala, depending on the context:
dot and parenthesis
Let’s quickly look at each of these.
optional class: In the first article of this series we saw how writing a class is optional. Scala allows you to write simple scripts and does not force you to create a class.
optional dot and parameter: We’re used to following a target object with a dot to indicate method invocation, as in lower.to(upper);. If we follow that convention, a simple loop will look like this:
However, that’s quite noisy and not as readable as the following:
When invoking methods on instances, you may drop the dot and the parentheses and enjoy the added fluency that Scala provides.
optional parameter names: When defining simple function values or code blocks, if you are merely passing the parameters to another function, you can let Scala do that for you. For example, instead of writing
you can write
Scala analyzes the parameter your code block should receive and analyzes the function you call in it and chains the parameter of your code block as the arguments for the function you call within the code block.
You can go a step further and make use of the optional dot to write
optional return: The last expression executed within a function is returned and you don’t have to place an explicit return. So the following
is equivalent to
Scala punishes us a little for placing the return by requiring us to specify the return type of the function. However, if you drop the return keyword, you can also drop the type information:
optional semicolon: You’re not required to end each statement or expression with a semicolon, Scala inserts semicolons automatically. Unlike Java code, Scala code usually has few semicolons.
optional try-catch: The Scala compiler does not distinguish checked vs. unchecked exceptions. If you’d like to handle an exception, place a try-catch block around the piece of code. If you’d rather let the caller of your code deal with it, simply do nothing and the exception will propagate to the caller. This eliminates clutter in the code and also avoids the temptation to suppress exceptions to silence the compiler.
When programming in Scala you will see these optional items interplay nicely to reduce code and clutter and make the code more expressive.
Until next time, I hope you play with Scala to enjoy its expressive and concise style.
Dr. Venkat Subramaniam is an award-winning author, founder of Agile Developer, Inc., and an adjunct faculty at the University of Houston.
He has trained and mentored thousands of software developers in the US, Canada, Europe, and Asia, and is a regularly-invited speaker at several international conferences. Venkat helps his clients effectively apply and succeed with agile practices on their software projects.
Venkat is the author of .NET Gotchas, the coauthor of 2007 Jolt Productivity Award winning Practices of an Agile Developer, the author of Programming Groovy: Dynamic Productivity for the Java Developer and Programming Scala: Tackle Multi-Core Complexity on the Java Virtual Machine. His latest book is Programming Concurrency on the JVM: Mastering synchronization, STM, and Actors.