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
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
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.
val x : Int = 23456; val y : Long = 7483; val z : Char = 'Z';
So, the syntax for defining a value or a variable is like the examples shown above. We first
var keyword. Then we use a name for the identifier. Then we use a colon and then a
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.
val x : Int = new Int(23456);
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.
val u : Unit = ();
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
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
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
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.
val s: String = new String("ABC");
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.
val s : String = null val l : List[Int] = null;
Now let's come to the
Nothing is at the bottom of every Scala class including value classes. The
Nothing is not like a
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.
val i:Int = null;
Since you can't instantiate Nothing, so you can't even assign Nothing to a value. So,the below statement is also not legal.
val i:Int = nothing;
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.
val i = 5 + 3 val d = 5 + 3.0 val s = "text"
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.
def fun(x) = x + 4
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.
def fun(x:Int) = x + 4
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.
def fac(n: Int) = if (n == 0) 1 else n * fac(n - 1)
If the condition is true, Scala knows that you get an Integer, but when the condition is false,
it doesn't know the return
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.