Simplifying SCALA 3 features
Scala 3 aka Dotty is a major release version of Scala programming language.
This version introduces the power features of Scala so far if we compare it with its version 2. x.
We are going to discuss the following in detail:
- Optional Braces
- Union Types
- Intersection Types
- Trait Parameters
- Enumeration
We have had the practice of using {...}
inside the brackets, we needed to indicate the code block within the block. Scala 3 gives us a provision where we can write it with indentation and it can be considered the total code block within that indentation much like (the Python style of coding).
Scala 2.x style
val age = 18
val eligibleForVote = if( age >= 18) {
"eligible"
} else {
"not eligible"
}
even more compact
val eligibleForVote = if(age >= 18) "eligible" else "not eligible"
scala 3 (Dotty version) has introduced one more keyword then
to simplify the block of code, especially for if-else
val eligibleForVote =
if(age >= 18) then
"eligible"
else
"not eligible"
between then
and else
we can consider the code block which can be evaluated for if block.
Another example of option braces can be for comprehension
Scala 2.x version
val forCompExample = for {
ids <- List(1,2,3,4,5)
names <- List("Robert","Mike", "John", "Peter", "Jacob")
} yield s"$ids $names"
Scala 3 Cleaner version
val forCompExampleV2 =
for
ids <- List(1,2,3,4,5)
names <- List("Robert","Mike", "John", "Peter", "Jacob")
yield s"$ids $names"
Similarly, it can apply to match
, methods
also.
Except omitting the {...}
, we can see some examples where we can use :
also to point out the beginning of the block for class
, traits
etc.
trait A: // points that it starts the block for traits
def f: Int
And if we want to implement the same trait A:
we can simply use the below code:
class C(x: Int) extends A:
def f = x
Likewise, we can use it for package
, object
, enum
, anonymous class
etc.
object O:
def f = 3
enum Color:
case Red, Green, Blue
new A:
def f = 3
package p:
def a = 1
package q:
def b = 2
Union Types
There are new types defined in the dotty version, one of them is the Union types.
A union type A | B
includes all values of both types.
trait Detail
case class ID(id:Int) extends Detail
case class Name(name:String) extends Detail
def help(detail: ID | Name) =
val user = detail match
case ID(id) => lookupID(id)
case Name(name) => lookupName(name)
Union types are duals of intersection types, meaning A|B
is same as B|A
.
The compiler assigns a union type to an expression under two conditions:
- When the type is specified explicitly
- When common supertypes of all alternatives is transparent
Examples:
scala> val id = ID(123)
val id: ID = ID(123)
scala> val name = Name("Jacob")
val name: Name = Name(Jacob)
scala> if true then name else id
val res1: Name = Name(Jacob)
scala> val either: ID | Name = if true then name else ID
val either: Name | ID = Name(Jacob)
You can change inference behavior using transparent
keyword
transparent trait Detail
scala> if true then name else id
val res2: Name | ID = Name(Jacob)
Intersection Types
Similar to Union types it also operates on type only and the operator we use is &
.
The type A & B
represents values that are of the type A
and B
at the same time.
Examples
trait Order:
def placeOrder(): Unit
trait Invoice:
def printInvoice(): Unit
def f(x: Order & Invoice) =
x.placeOrder()
x.printInvoice()
Trait Parameters
We can now pass the parameters to traits the same as class and case class.
trait Greeting(val name: String):
def msg = s"How are you, $name"
class C extends Greeting("Bob"):
println(msg)
You can read this in detail here on how to avoid ambiguity for multiple extents.
Enumeration
Until the Scala 2. x version we used to create the enums using either an Enumeration class (much like Java style) or using a case class. It had several challenges when using it for pattern match for exhaustive search, and also it used to have NoSuchElementException
due to withName
API of scala.Enumeration
.
scala 2.x example
object WeekDay extends Enumeration {
type WeekDay = Value
val Mon, Tue, Wed, Thu, Fri, Sat, Sun = Value
}
import WeekDay._
def isWorkingDay(d: WeekDay) = ! (d == Sat || d == Sun)
WeekDay.values filter isWorkingDay foreach println
Scala 3 has a cleaner approach to creating and using the enum
enum Permission:
case Read, Write, Execute
This defines a new sealed
class.
We can access each value like Permission.Read
, Permission.Write
, Permission,Execute
.
All the values are part of the companion object.
Passing Param to enum
Enum can be parameterized
enum Permission(val value: Int):
case Read extends Permission(1)
case Write extends Permission(2)
case Execute extends Permission(3)
References: