Exploring Scala 3: Practical Examples and Key Features

Scala 3, the latest evolution of the Scala programming language, brings with it a host of exciting features that can revolutionize the way you write code. In this article, we'll dive into some practical examples to demonstrate the key features of Scala 3 and how they can benefit your development projects.

1. Simplified Syntax for Better Readability

Scala 3 introduces a cleaner and more consistent syntax, making your code easier to read and write. Let's take a look at a simple example of pattern matching:

// Scala 2
def processNumber(num: Int): String = num match {
  case 0 => "Zero"
  case 1 => "One"
  case _ => "Other"
}

// Scala 3
def processNumber(num: Int): String = num match
  case 0 => "Zero"
  case 1 => "One"
  case _ => "Other"

The removal of unnecessary braces enhances the readability of the code.

2. Union Types and Type Aliases

Union types allow a variable to have multiple possible types, offering greater flexibility in modeling data. Let's say you're working with a function that might return an integer or a string:

// Scala 3
def processValue(value: Int | String): Unit =
  println(value)

Additionally, type aliases become more versatile:

// Scala 3
type Name = String | Null

def printName(name: Name): Unit =
  println(name.getOrElse("No name"))

3. Given and Using Clauses for Dependency Injection

The given and using clauses simplify dependency injection and context passing. Consider a scenario where you want to provide different implementations of a service:

// Scala 3
trait DataService:
  def getData: List[String]

given liveDataService as DataService =
  new DataService {
    def getData: List[String] = List("Data from live service")
  }

given testDataService as DataService =
  new DataService {
    def getData: List[String] = List("Test data")
  }

def processData(using dataService: DataService): Unit =
  println(dataService.getData)

4. Extension Methods for Better Code Organization

Extension methods allow you to add new methods to existing types without modifying their original code. Let's say you want to add a capitalize method to strings:

// Scala 3
extension (str: String)
  def capitalize: String =
    str.head.toUpper + str.tail.toLowerCase

val message = "hello, scala 3 extensions!"
println(message.capitalize)

5. Improved Interoperability

Scala 3 enhances its compatibility with Java and other JVM languages. You can seamlessly interact with Java classes and libraries:

// Scala 3
import java.util.{ArrayList, List}

val javaList: List[String] = ArrayList[String]()
javaList.add("Hello")
javaList.add("Scala 3")

println(javaList)

6. Dependent Function Types for Precise Type Signatures

Dependent function types allow functions to return types that depend on their input values. Here's a simple example:

// Scala 3
def createArray(size: Int): Array[Int] =
  new Array(size)

val myArray = createArray(5)
println(myArray.length)

Conclusion

Scala 3 introduces practical enhancements that streamline your code, improve type safety, and make your development experience more enjoyable. By embracing its features, you'll write more readable, maintainable, and expressive code. As you explore and experiment with Scala 3, you'll discover a new world of possibilities for modern software development.