Scala Tutorials Part #7 - Objects Everywhere
Originally Posted On : 29 Dec 2016 Last Updated : 26 Mar 2017
In this post we discuss on why the concept of an object in scala is more prevalent and what are its advantages. Examples are mostly with data types since they are fundamental to the language.
This is part 7 of the scala tutorial series. Check here for the full series.
- Data types as Objects
- Operations on types
- Creation of custom types
- Language constructs vs Library Abstractions
- Java’s data type boxing/unboxing compared
- Bigint example
- Value comparison
- Implementations in other languages and some notes
Scala is a multi-paradigm language, mainly a combination of object oriented and functional. To support this kind of a language, it is necessary to formulate an idea around which things are built. As we saw earlier, in scala there are no primitive types and everything is an object. This is not be confused with singleton objects which we saw in part 4, but object as in instance of a class. In the rest of this article an object means an instance of a class an in regular java speak.
We have seen before that these two concepts i.e object oriented and functional are kind of orthogonal to each other. I am not going to give a exhaustive list on what are all objects and what are not, but I will be explaining the idea and advantages behind it and why it is important.
There are no native data types in scala and all of the data types have a class of their own. Now how would you go by designing something as fundamental as a data type ? Turns out that all of the data types map to native data types in java whenever it seems appropriate. We will take a look at one example i.e Int since it is simpler to explain and the same idea extends to almost all of the types.
You can try decompiling a class containing an
Int and see it compiles to
public static int i.e the java primitive type int.
All of the data types in scala except
String are present inside the package scala
Below is a list of data types that convert directly to native types on the JVM
|Scala type||Runtime mapping|
Scala variables do not have any additional overhead of creating objects, they all map to a native type in the JVM.
We have understood that all base types are objects in scala. The next important thing that comes to mind is that how will one do operations on it ? Since adding two objects is not possible, scala resorts to something called synthetic methods which we saw earlier in case classes.
All operations that we do in primitive java types such as
* etc ., are implemented as methods. Let’s take Int.scala for example and look at how addition is implemented.
Many things can be inferred from the above code snippet and
- Above code sample defines all of the possible addition operations for other types with the
Inttype. Other type operations for
Doubleetc have the same kind of logic.
- The above definition is inside of an abstract class and it is final hence cannot be extended.
- As we saw in the second part, all the variables with value types extend
AnyValwhich again extends from
All of the mentioned methods which define the fundamental operators are abstract. So how does it get implemented ?
If you try the same with regular classes you will get an error of course.
So how does it work and gets translated to a native type ? It’s all compiler magic. As usual, lets understand by de-compiling a few classes. Remember that scala type classes have special meaning since they follow a hierarchy and that is the reason why the Int.scala is abstract but still we are able to use it.
Let’s consider the below class with a custom
+ function implemented.
This gets compiled as below.
+ gets compiled to
$plus since the former is not a legal definition as per java.
Let’s see another example.
It gets decompiled as,
.+ gets compiled to same to the
This way of calling a class member without the dot operator is called
infix notation which we see later in a dedicated tutorial.
Since the magic is done while compilation, the
+ by itself is represented as a synthetic function.
We looked at how
Int.scala was coded. But how would you go about creating something fundamental as this without the help of the compiler.
If you took the course in Coursera “Functional Programming principles in Scala - by Martin Odersky”, then you would be familiar with this. Nonetheless, below is the video where he explains it in a succinct way.
If by any chance the above listed youtube video is taken down, just sign up for the course in Coursera, even if you don’t need the certificate you can audit the course/see the videos. This video is listed as Lecture 4.1 in the course.
There might be some parts of the video that you might not fully understand, just ignore them for them, I will definitely cover them later.
This should give you an intuition on why such a choice was made in scala.
In java the operations such as
+ are built within the language which makes them difficult for customization.
Scala was built with such things in mind. In fact the name scala is derived from a portmanteau of the words “Scalable language” implying that it grows with the users demand. The term scalable does not necessarily mean it scales for performance, but rather a extensible/scalable as a language.
This will become more coherent with how the
BigInt class behaves in scala when compared to java as explained in the following sections.
Unlike scala, java has both primitive and boxed types. This is kind of ugly when you compare it with scala since everything is an object here.
We already saw how scala types convert to native JVM primitive types, but how do they work in java ?
Let’s take the below class for example.
It gets compiled to,
Integer.valueOfwraps the primitive types 10,30 to boxed types.
- The addition
i+kgets converted to
i.intValue + k.intValue.
intValuemethod returns the primitive type assigned to that boxed type.
So the above operation has created four instances of the Integer.java class. This has to be done since Integer.java is truly an object at runtime unlike scala’s int which gets converted to primitive type at compile time.
This distinction is important and will be crucial in understanding scala collections.
We will discuss two advantages about this whole everything is an object representation.
Apart from the advantage that scala does not create objects for native types, it is also offers convenient syntax for other types such as Bigint.
In java, to add two BigInteger types, we need to use special method calls.
Now this is fine for Big integers since we know that in java it is not a primitive type, but it is an additional tax imposed on developers.
In scala, since everything is an object and also the operators by themselves are just methods, adding two Big integers is pretty straightforward.
We have to represent them as strings since they are too big even for long type.
This offers a convenient syntax and we can use the same operators we use for our known data types.
One could view this as a convenience feature for developers, but this it is much more. As we venture more into the world of scala, we will also encounter more advanced data types such Algebraic types which are useful in Machine learning/math related computations. What is more interesting is since everything is represented as an object we can now abstract certain things which makes it easier for programmers to write types of their own.
Since all of the types are now objects/classes and they all follow the tree hierarchy from the top type that is
Any, they can have some common methods
which are useful for all types.
We will explore some of the below.
Each of the types will have type conversions to one or more other types. One need not worry about the current type under consideration since these methods are common and if there are any mistakes, they would be at compile time rather than run time.
In java, these kind of conversions is rather cumbersome.
This is another example of how if everything is an object is advantageous. One can even include a method such as
toRoman which converts the given value to a roman numeral. The programmer can choose which class to place this, an example would be Int.scala.
There are several more, but the point was to bring an intuition rather than an exhaustive listing.
In java you would have been taught not to use the
== operator for comparing object types since they would compare references.
With that said, since everything is also an object we can define our own method for the
Just like the
+ method above, the
== is a synthetic function in the scala library.
Above code compiles to native java like comparison i.e for string it would be the
equals method which does a character by character comparison underneath.
Turns out that scala is not the first language to implement this “objects everywhere” concept.
In Ruby there are no primitive types and they operate almost similar to scala except for that fact that scala is object oriented in a very pure form, which means that everything is an object.
In later tutorials we will explore on how this concept enables us to write custom methods in fundamental types such as
toRoman for example and custom types such as Algebraic data types
Part 8 i.e the next part, I will explain about another cool feature of scala which is traits (more cooler than case classes ?) and how it really takes Object oriented programming to the next level.
Stay tuned and happy new year