If you already know programming and willing to learn a new language, the best place to start is
the type system because that's
the most fundamental thing. The type system is involved everywhere whether you create some
variables,
constants, classes, methods or functions. So, let’s begin with the type system.
Scala type system is one of the most sophisticated in any programming language. To
understand
the internals of the Scala type system, you may need a long book. In this video, I will
introduce
you to enough basics that are necessary to grasp other language elements. Over next several
videos,
we will build a strong foundation in Scala and again come back to the type system in a later
video
to cover some advanced topics. So, let's start. The first question is this.
What is a type system and What does it do?
I guess you already understand it. The type system is a set of rules for various programming constructs such as variables, functions, and expressions. So, if you create an object of type Integer, you are imposing some rules on your object. These rules include what kind of values you can assign to the variable and what kind of operations are allowed on your variable? Similarly, if you create a function and define the return type as Boolean, you are again imposing some rules on your function. Right? Now, you may have further questions.
- What are those rules and who defines them?
Well, these rules vary from one language to other, and the creators of the programming language
are responsible for establishing
those rules using the type system. That's why we are learning Scala type system.
The next question.
- Who validates those rules and when are they validated?
Well, there are two options.
- Let the compiler verify those rules.
- Let the runtime environment check those rules.
So, if the language implements all the rule checking at compile type, we call it a statically
typed language. Scala falls
into this category. So, Scala is a statically typed language, and it will perform all the type
checking
at the time of compilation.
On the other side, if the language implements all the type checking at run-time, we call it
a
dynamically typed language. Python is an example of a dynamically typed language.
The type system is the protection for the programmers from writing improper code and get
unexpected
results. The statically typed languages like Scala will be able to detect the problems at
compile
time, so they offer an early protection. They also provide a better runtime performance because
the
compiler can apply some optimizations and they don't need to perform type checking at runtime.
Scala Type System
Let's take a quick look at Scala's type system.
Scala has a unified type system, and the hierarchy looks like the one shown in below image.
It starts with the top-level class Any. So, anything in Scala is a subclass of this Scala class. At the next level, Scala type system breaks into two groups.
- Value Classes
- Reference Classes
Scala Value Classes
Let's first look into the value classes.
There are nine built-in value Classes, and they are the fundamental data types of Scala
language.
You might find them similar to Java's primitive data types, but in Scala, they are all
well-defined
Classes.
Typically, when you want to create an instance of a Class, you would be using the
new keyword. But Scala value classes don't need a
new keyword. In fact, that's illegal.
So, all these Value Classes are instantiated using literals. Let me show you some examples.
So, the syntax for defining a value or a variable is like the examples shown above. We first
use the
val or
var keyword. Then we use a name for the identifier. Then we use a colon and then a
data
type. Finally, you initialize it with a valid literal. That's it.
If you try to instantiate them using a
new keyword, you should get an error. Try the example shown below.
The value class unit is little different from other classes in this hierarchy. We already learned that unit is roughly similar to void in Java. We use the unit type as a return type of a method or function that doesn't return anything. But, remember that unit is not a void. It has a value, and we represent it using an empty parenthesis.
You must notice one important thing in thefigure shown above. The
value classes are a direct child of
AnyVal. They don't inherit any other class. Those dotted lines represent an implicit
conversion.
So, a
Byte can implicitly convert to an
Int. Similarly, an
Int can turn to a
Long, and then to a
Float and finally to a
Double. You don't need to cast them explicitly. Whenever we use these values in an
expression,
and it requires a conversion, Scala will do an implicit conversion.
The diagram shows only nine, but there are many other value classes. In fact, you can
create
a new value class, and that also fits into the same graph. One example of the other value class
is
a
RichInt
Class. This diagram doesn't show the
RichInt, but I can show you that in the
Scala
documentation
Let's check the type hierarchy. So,
RichInt is a subclass of
AnyVal, and it supports implicit conversion from an
Int.
RichInt is just one. There are many others. These are all known as booster classes. I
will
come back to them once again, but for now, just remember that these booster classes are there
to
provide some additional functionalities through implicit conversion.
For example,
abs is a method that returns an absolute value. But the
abs method is not defined on an
Int class. It is part of a
RichInt class. But you can use it without any typecasting because Scala implicitly
converts
it to
RichInt.
So, in this example, Int is implicitly converted to RichInt. Similarly, RichLong offers an implicit conversion for a Long.
Scala reference Classes
Now, let's turn our, focus to the reference classes. As you can see, all Reference Classes
rolls up to a common parent
AnyRef. In fact,
AnyRef is just an alias for
java.lang.Object. So, all the classes written in Java, as well as written in Scala are
part
of this hierarchy. As you the figure shows,
String is a reference class.
Since these are standard classes, you can use
new keyword to instantiate a
String object.
Scala Null and Nothing
Now the last thing on the hierarchy. You see these two bottom types. The Null and the Nothing. They are special purpose classes. Every reference class has a Null class at their bottom. The Null class can be instantiated using a literal Null. The literal Null allows us to assign a Null to a reference class in Scala. Let me show you an example.
Now let's come to the
Nothing. The
Nothing is at the bottom of every Scala class including value classes. The
Nothing is not like a
Null.
Nothing means nothing. You can't create an instance of
Nothing so you can't assign
Nothing to a value or a variable.
If you notice, we have a
Null Class between
Nothing and reference classes. But for value class, the
Null is not there. The
Nothing is the direct child of every value class, and this hierarchy has one important
implication.
- You can't assign a null to any value class because null is not a subclass of their hierarchy. So, the below statement is not legal.
Since you can't instantiate Nothing, so you can't even assign Nothing to a value. So,the below statement is also not legal.
What does it mean?
That means you can't create a value in Scala without initializing it with some valid value
other
than
null and
nothing. The
Null and the
Nothing are not allowed for a value class. However, you can assign
null to a reference class.
You might be wondering if we can't instantiate a
Nothing then what is the purpose of
Nothing? I will cover that in a separate topic.
Scala type inference
Scala has a built-in type inference mechanism. So, you can leave the type annotation and let Scala infer it automatically. This approach eliminates a lot of typing and keeps the code short and precise. Let's see some examples.
You can see that Scala is smart enough to infer the types. But this inference is not possible in many other cases. Let's see an example.
There is no way to identify the type of a function parameter. The parameter x may be an integer or may be a double. So, it is mandatory to specify the type annotation for function parameters.
In this example, Scala is still able to infer the return type of the function. It returns an integer. But sometimes, even that is not possible. Here is an example.
If the condition is true, Scala knows that you get an Integer, but when the condition is false,
it doesn't know the return
type.
So, we learned two scenarios where specifying the type annotation is mandatory.
- Function parameters.
- The return type of a recursive function.
You will learn some more such scenarios as you progress with the tutorial and start practicing
Scala language. Every other
place, you can skip the type annotation until Scala compiler cries with an error.
Good, here is a list of key takeaways of this session.
- Scala is a statically typed language.
- Scala has a unified type system. Every Scala class rolls up to Any.
- We can further classify Scala type system into two categories.
- Value Classes (AnyVal)
- Reference Classes (AnyRef)
- The nine fundamental value types are
- Double, Float, Long, Int, Short, Byte
- Char, Boolean, Unit
- You cannot use new keyword to instantiate value classes.
- The new keyword is only allowed for reference classes.
- Most of the classes that you will be creating should be a reference class. However, you can also create value classes.
- We use the unit type as a return type of a function that doesnot return a meaningful value.
- The value class hierarchy is flat, they all are a subclass of AnyVal.
- Implicit conversion is one of the key features of Scala types. We will learn more about it in a later video.
- There are many booster classes such as RichInt, RichLong, RichDouble. They offer additional functionality using implicit conversion.
- The Null is the child class of every Reference class, so we can assign null to any reference class.
- The Nothing is the child class of every Scala class including value classes and reference classes.
- We cannot instantiate the Nothing, but it has some other purpose. We will come back on this topic later.
- You can't Instantiate a value class without initializing it with a valid value other than Null and Nothing.
- Scala has a built-in type inference mechanism which allows the programmer to omit type annotations in most of the cases.
- Finally, Remember the diagram. You should also notice that all thosetypes reside in a package named Scala. Only the String type is there in the package java.lang. You don't need to import these two packages. Scala will automatically import them.
That's it. We covered fundamentals of Scala Types. I will come back on this topic once again to
cover few more things.
Thank you for watching Learning Journal. Keep Learning and Keep growing.