What’s New in Scala 3?
After it was first released in 2004, Scala has seen a wild rate of growth, with companies like Apple, LinkedIn, Twitter, and Walmart as some of its prominent adopters. The 2020 StackOverflow developer survey ranked Scala developer as one of the top-paid categories in the industry. The survey also ranked Scala #14 on the list of the languages most loved by software engineers. This adoption is surely going to increase in the future with the release of Scala 3 pitched as a big step towards realizing the full potential of the fusion of the OOP and FP paradigms in a typed setting. With the new release right around the corner (end of the year 2020), let's take a look at the major ideas behind this design overhaul.
A Language Based on DOT Calculus
The DOT Calculus (Dependent Object Types) is a new calculus developed by Dr. Martin Odersky’s group. DOT normalizes Scala’s type system by unifying the constructs for type members and simplifies the greatest lower bound and least upper bound computations by providing intersection and union types. This will provide powerful type manipulation capabilities and open new avenues in domain-driven design.
Dotty: The New Scala Compiler
The new compiler is rewritten from scratch for speed and correctness. The language has undergone a lot of changes to undergo simplification.
Stuff That Dies
Several unpopular and redundant features have been dropped, of which some of the prominent ones are:
These steps have been taken to make Scala more user friendly and save early adopters from getting discouraged by the complexities and pitfalls of these features.
There is also a plethora of new features introduced in Scala 3, some of which include:
Now anything can be declared at the top level, including types, variables, and functions. There is no longer a need to package things up in an object. Also, any function can now be declared as the Main method using the
@main annotation, hence obscuring the need to extend the
Let’s take a look at this code, which works perfectly fine with the Dotty compiler.
Scala 3 introduces a fourth way of invoking a function, called creator applications, which allows you to create an instance of a type without using the
new keyword and without the need to write
Extensions methods allow us to add extra functionality to existing types, and they come in handy if we can’t or don’t want to modify a type. Extensions can be applied to generic types and can be operators as well.
Let’s try it out ourselves:
Finally, Scala 3 introduces support for first-class enums. Scala 2 had a kind of indirect way to support this feature through case classes and
Enumeration class, but now it finally supports them in the Java style.
Additionally, we can now add user-defined methods to the
New Ways to Manipulate Types!
Scala 3 gives us a much richer toolkit for describing data by letting us define types in a much more descriptive way. Let's have a look at some of the new types introduced.
Types can now be defined as a union of other types, and they don’t necessarily have to share a common base class. Let’s understand this with a simple use case: You have to buy a new smartphone, and the reason you choose one can be either its camera, processor, or operating system. Let's take a look at how we can cook this up in Scala 3.
Scala 3 also have intersection types now. To put it simply, if we think of the union of types as either
B, then intersection can be thought of as both
B. Here’s an example:
We can now literally define any literal value as a type with a singleton instance of itself. Simply put, a literal value like
true can now be treated as a type in itself. Let’s understand this with an example:
This one is particularly useful when we are working with domain-driven design, and we often have to define new types as wrappers over other types. For example, we may define a type called
ItemID which is actually just a wrapper over the
String type with a restriction that the first character needs to be in uppercase.
The problem with using wrapper types for this is that we incur unwanted overhead when accessing their fields or methods, or composing these types. And these overheads start to add up if we have a lot of such entities in our design.
This is where the
opaque type comes in. It allows us to define
ItemID as being a
String with the provision to add additional functionality/restrictions (through a companion object). So, if we were to code the above problem using
opaque type, we can code it like this:
Implicits are replaced with given and using clauses
Implicits were some of the most powerful features in Scala, allowing code abstractions. They were an essential tool for using type classes and were the central point for libraries, like Cats and shapeless. But this also introduced unwanted complexities in a large code base when it became difficult to track what the compiler was injecting.
Scala 3 introduces
given instances and
using clauses to break the unified scheme of the
implicit keyword, giving much more control over what is injected in our code. Let’s take a look at a small example:
While this is just a basic example, I encourage you to check out the Scala 3 docs linked above to gain a deeper understanding.
Our favorite language, Scala, has undergone a major design overhaul and in a way, it can be considered as a new language altogether. It will be exciting to see new FOSS projects coming up, and I hope this article has given you a headstart if you are planning to start one yourself.
While this article gives a concise overview of the changes which I feel are prominent, it is by no means a complete guide to all the new things coming in with Scala 3. I encourage you to head over to the official docs to get a better overall understanding.
Until next time. Ciao!