iTranslated by AI
Unique Syntax Features in Scala
This article is for Day 16 of the Programming Language Specific Syntax Advent Calendar 2025.
I will introduce them based on my personal preferences.
Since I'm currently studying Scala, this content is at an introductory level.
Sample code for binary search
I've implemented it while intentionally utilizing the language's features.
// Scala - Pattern matching + Option + Infix notation
def binarySearch[T: Ordering](arr: IndexedSeq[T], target: T): Option[Int] = {
import Ordering.Implicits._
@annotation.tailrec
def go(left: Int, right: Int): Option[Int] = {
if (left > right) None
else {
val mid = left + (right - left) / 2
arr(mid) match {
case x if x == target => Some(mid)
case x if x < target => go(mid + 1, right)
case _ => go(left, mid - 1)
}
}
}
go(0, arr.length - 1)
}
@main def run(): Unit =
println(binarySearch(Vector(1, 3, 5, 7, 9), 5).getOrElse(-1)) // 2
Pickup Syntax
implicit parameter (given/using in Scala 3)
You can implicitly pass parameters or define instances of type classes.
// Implicit arguments
def sort[T](list: List[T])(implicit ord: Ordering[T]) = list.sorted
// Scala 3
def sort[T](list: List[T])(using ord: Ordering[T]) = list.sorted
// Type class instance
given Ordering[Person] with
def compare(a: Person, b: Person) = a.age - b.age
It's convenient, but it looks like it might make the code hard to follow.
for-comprehension
You can combine multiple collections to generate a new collection.
// List comprehension
val doubled = for (x <- 1 to 10 if x % 2 == 0) yield x * 2
// Multiple generators
for {
x <- 1 to 3
y <- 1 to 3
if x != y
} yield (x, y)
// Syntactic sugar for flatMap/map
for {
a <- Future(1)
b <- Future(2)
} yield a + b
Extension Methods (Scala 3)
You can add new methods to existing types.
extension (s: String)
def greet: String = s"Hello, $s!"
def times(n: Int): String = s * n
"World".greet // "Hello, World!"
"ab".times(3) // "ababab"
It's good because it allows for concise writing.
Infix Notation
You can call a method with a single argument using infix notation.
// Calling a method using infix notation
1 to 10 // 1.to(10)
list map f // list.map(f)
a :: b :: Nil // Nil.::(b).::(a)
// Custom infix method
case class Vec(x: Int, y: Int) {
def +(other: Vec) = Vec(x + other.x, y + other.y)
}
Vec(1, 2) + Vec(3, 4) // Vec(4, 6)
Pattern Matching
You can perform powerful branching based on types and values using match expressions.
// match expression
val result = value match {
case 0 => "zero"
case 1 | 2 => "one or two"
case n if n < 0 => s"negative: $n"
case _ => "other"
}
// Decomposition of case class
case class Person(name: String, age: Int)
person match {
case Person("Alice", age) => s"Alice is $age"
case Person(name, age) if age >= 18 => s"$name is adult"
case _ => "unknown"
}
I feel that pattern matching expressions are richer compared to other languages.
Option
You can represent the presence or absence of a value in a type-safe manner using Some and None.
// Some or None
val opt: Option[Int] = Some(42)
// Pattern matching
opt match {
case Some(x) => println(x)
case None => println("empty")
}
// Method chain
opt.map(_ * 2).filter(_ > 50).getOrElse(0)
// for-comprehension
for {
x <- Some(10)
y <- Some(20)
} yield x + y // Some(30)
Discussion