1. Overview

In this tutorial, we’ll learn about type inference in Scala look into its different types. Then, we’ll learn some of its limitations to know when it’s best to avoid using it. We’ll also see some interesting examples along the way.

2. Type Declaration Is Optional in Scala

The Scala compiler can infer the types of expressions automatically from contextual information. Therefore, we need not declare the types explicitly. This feature is commonly referred to as type inference. It helps reduce the verbosity of our code, making it more concise and readable. In Scala, we can see type inference in variable declarations, function return types, and parameters of anonymous functions.

Let’s learn each one of them in detail.

3. Type Inference for Variables

The most common application of type inference is seen in variable declaration. We can omit the type in declarations as the compiler can guess the type based on the initial value we give. For instance, if the initial value is in double-quotes, the compiler assumes the type to be String.

If it’s in single quotes, it takes the type as a Char, and so forth. Let’s declare a variable and give it an integer value and see what happens:

val integerObj = 10
println(integerObj.getClass)  // prints int

As expected, we can see the type of variable is printed as int. Now, let’s look at a few more examples with other basic types:

val doubleObj = 10.01   // infers double type
val charObj = 'a'       // infers char type
val stringObj = "hello" // infers java.lang.String type
val booleanObj = true   // infers boolean type

4. Type Inference for Functions

Similar to the variable type inference, the Scala compiler can infer the return types of functions as well. Let’s look at a simple function for calculating the square of an integer value and omit its return type:

def squareInt(num:Int) = {
  num * num
}

Now, let’s call the function and check what type it returns:

val square = squareInt(2)
println(square.getClass)    // prints int

Another thing we can notice is the absence of the return keyword. We must omit the return keyword as well when we choose type inference for the return type. Otherwise, we get an error during the compilation time.

However, in the case of recursive functions, the compiler gets confused and won’t be able to detect the return type. We’ll learn this limitation in detail in the upcoming sections.

5. Type Inference for Parameters

By default, Scala cannot infer the types of function parameters. Nevertheless, there are some exceptions to this rule. In the case of anonymous functions, the Scala compiler can infer the parameter types.

Let’s calculate the sum of a list of elements using a reduce function:

val list = List(1,2,3,4)
val sum1 = list.reduce((a:Int,b:Int) => a + b)
val sum2 = list.reduce((a, b) => a + b)

Here, we can see the reduce function takes an anonymous function with two parameters, a and b. In the first expression, we’ve declared the types explicitly. In the second one, we’ve omitted the parameter types. The types of parameters a and b are inferred from the element types of the input list.

Both expressions are valid and give us the same results. We can see the second expression is more clean, concise, and elegant than the first one.

6. Limitations of Type Inference

The type inference feature is useful most of the time. It helps us write clean and concise code. However, sometimes we need to explicitly declare the type – for example, if we need a specific data type when declaring a variable or in the case of defining recursive functions.

All non-decimal numbers are inferred as integer types, and all decimal values are inferred as double types. This works in most of our use cases. But, in certain cases, we need more specific type declarations. Let’s assume we need to declare a variable for byte values:

val byteVarDeclared:Byte = 1

Here, we can see that we have to declare the type explicitly, else the compiler will assume an int type.

Now, let’s look at a recursive function definition:

def sumRecursive(list:List[Int]):Int={
  if(list.size > 1) {
    list.head + sumRecursive(list.drop(1))
  } else {
    list.head
  }
}

If we try to omit the return type of this function, we get a compilation error: “recursive method sumRecursive needs result type“.

7. Conclusion

In this article, we’ve learned how the type inference feature works in Scala. We learned that the Scala compiler can infer types in variable declarations, function return types, and, in some cases, the function parameters as well. We’ve also seen some limitations where it cannot apply type inference.

As always, the whole code in this article is available over on GitHub.