Fundamental Types

Even though you can use any Java type in Scala, you can also enjoy a few types native to Scala. Scala makes a clearer distinction between value types and reference types and also goes a few extra miles with type definitions to enhance type verification and inference. Let’s get a grasp of these fundamental types since you’ll encounter these types in Scala quite often.

The Any Type

Scala’s Any type is a superclass of all types, as you can see here.

images/UsingTypeInference/scalatypes.png

Any can serve as a common reference to objects of any type. Any is an abstract class with the following methods: !=, ==, asInstanceOf, equals, hashCode, isInstanceOf, and toString.

The direct descendants of Any are AnyVal and AnyRef. AnyVal stands as a base for all types in Scala—for example, Int, Double, and so on—that map over to the primitive types in Java. On the other hand, AnyRef is the base for all reference types. Although AnyVal does not have any additional methods, AnyRef contains the methods of Java’s Object such as notify, wait, and finalize.

AnyRef directly maps to the Java Object, so you can pretty much use it in Scala like you’d use Object in Java. On the other hand, you can’t call all the methods of Object on a reference of Any or AnyVal, even though internally Scala treats them as Object references when compiled to bytecode. In other words, while AnyRef directly maps to Object, Any and AnyVal are type erased to Object. This is much like the way generics are type erased in Java.

While Any is at the top of the hierarchy, the bottommost type is Nothing.

Something about Nothing

Nothing in Scala is everything. It’s easy to see why we need Any, but Nothing seems rather odd at first, especially since it can stand in for any class.

Nothing is quite important to support the type verification mechanics of Scala. Scala’s type inference works hard to determine the type of expressions and functions. If the type inferred is too broad, it won’t help type verification. At the same time, we do want to infer the type of an expression or function where one branch returns, say, an Int and another branch throws an exception, like in the following example:

 
def​ someOp(number : ​Int​) =
 
if​(number < 10)
 
number * 2
 
else
 
throw​ ​new​ RuntimeException(​"invalid argument"​)

In this case, inferring the return type of the function as Any would be too broad and useless. It’s more useful to infer the return type as Int. It’s easy to see that the arithmetic expression evaluates as Int. Furthermore, the branch that throws the exception must be inferred, in this case, to return an Int or a subtype of Int for it to be compatible with the inferred return type. However, throw can’t be treated arbitrarily as an Int type since an exception may occur just about anywhere. Nothing comes to the rescue—it makes type inference work smoothly by acting as a subtype of all types. Since it’s a subtype of all types, it is substitutable for anything. Nothing is abstract, so you won’t ever have a real instance of it anywhere at runtime. It’s purely a helper for type inference and verification purposes.

Let’s explore this further with an example. Let’s take a look at a method, named madMethod, that throws an exception and see how Scala infers the type, using the REPL:

 
scala> def madMethod() = { throw new IllegalArgumentException() }
 
madMethod: ()Nothing
 
 
scala> :quit

The interactive REPL session reveals that Scala inferred the return type of an expression that throws an exception as Nothing. Scala’s Nothing is actually quite something—it is a subtype of every other type. So, Nothing is substitutable for anything in Scala.

While Any is the mother of all classes, Nothing can stand in for anything.

Option Type

Remember the sage advice in Joshua Bloch’s Effective Java [Blo08], to return an empty collection instead of a null reference. If we follow that advice, we don’t have to endure NullPointerException, and iteration becomes easy even when the resulting collection is empty. That’s good advice to follow when working with collections, but we need something similar to follow when working with other return types.

When you perform pattern matching, for example, the result of the match may be an object, a list, a tuple, and so on, or it may be nonexistent. Returning a null quietly is problematic in two ways. First, the fact that there may not be a result is not expressed explicitly. Second, there is no way to force the caller of the function to check for nonexistence or null.

Scala goes a step further to specify nonexistence. With Scala’s Option[T] you can program with intention, and specify that you do actually intend to return no result. Scala achieves this in a type-safe manner so the check can be enforced at compile time. Let’s look at an example of using this special type:

MakingUseOfTypes/OptionExample.scala
 
def​ commentOnPractice(input: ​String​) = {
 
//rather than returning null
 
if​ (input == ​"test"​) Some(​"good"​) ​else​ None
 
}
 
 
for​ (input <- Set(​"test"​, ​"hack"​)) {
 
val​ comment = commentOnPractice(input)
 
val​ commentDisplay = comment.getOrElse(​"Found no comments"​)
 
println(s​"input: $input comment: $commentDisplay"​)
 
}

Here, the method commentOnPractice may return a comment of type String, or may not have any comments at all. If we were to return a null, we’re at the mercy of the receiver to perform a null check. This is a burden; in addition the code would be smelly and error prone.

Instead of returning an instance of String, the method commentOnPractice returns either an instance of Some[T] or a None. These two classes extend from the Option[T] class. The code that receives an instance of Option[T] would have to fetch the result, with a clear expectation that the result may be nonexistent. In the previous example, we used the getOrElse method of Option[T], where we provide an alternative if the result did not exist. Let’s exercise the previous code and take a look at the output:

 
input: test comment: good
 
input: hack comment: Found no comments

By making the type explicit as Option[String], Scala forces you to check for the nonexistence of an instance. Since this is enforced at compile time, you’re less likely to get a NullPointerException because of unchecked null references. By calling the getOrElse method on the returned Option[T], you can proactively indicate your intentions in case the result is nonexistent—that is, None.

Either

The Option type is useful when the result of a function call may or may not exist. Sometimes you may like to return one of two different values from a function. Scala’s Either comes in handy for this purpose.

Suppose a compute method performs some calculations for a given input, but it’s valid only for positive integers. For invalid input, instead of throwing an exception, we could gracefully return an error message. Either has two values: the left value (which is considered an error) and the right value (which is considered a correct or expected value). Let’s write the example compute function.

MakingUseOfTypes/UsingEither.scala
 
def​ compute(input: ​Int​) =
 
if​(input > 0)
 
Right(math.sqrt(input))
 
else
 
Left(​"Error computing, invalid input"​)

The compute method checks if the input is valid and if it is, returns a valid result wrapped into the right part of Either using the Right singleton. On the other hand, if the input is invalid, it returns the error details as the left part of Either using the Left singleton.

When you receive an Either you can extract the content using pattern matching, as in this displayResult function:

MakingUseOfTypes/UsingEither.scala
 
def​ displayResult(result: Either[​String​, Double]) = {
 
println(s​"Raw: $result"​)
 
result ​match​ {
 
case​ Right(value) => println(s​"result $value"​)
 
case​ Left(err) => println(s​"Error: $err"​)
 
}
 
}

We first display the values in the result as is. Then we extract the value using pattern matching to display the right part or the left part, as appropriate.

Let’s call the displayResult function:

MakingUseOfTypes/UsingEither.scala
 
displayResult(compute(4))
 
displayResult(compute(-4))

The output from this code is shown here:

 
Raw: Right(2.0)
 
result 2.0
 
Raw: Left(Error computing, invalid input)
 
Error: Error computing, invalid input

If you want to convey the possible nonexistence of a value, use Optional, but to vary between possibly two different values, use Either.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.138.181.145