Getting started
Introduction
Scala is a modern multi-paradigm programming language designed to express common programming patterns in a concise, elegant, and type-safe way. It seamlessly integrates features of object-oriented and functional languages.
Basic Syntax
// Immutable variable (preferred)
val x = 10
val name: String = "Scala"
// Mutable variable
var counter = 0
counter = 1
// Method definition
def add(a: Int, b: Int): Int = a + b
def greet(name: String) = s"Hello, $name"
// Type inference
val inferred = "I'm a String"
Quick Example
// Case class (immutable)
case class Person(name: String, age: Int)
// Pattern matching
def describe(p: Person) = p match {
case Person(n, a) if a < 18 => s"$n is a minor"
case Person(n, a) => s"$n is $a years old"
}
val john = Person("John", 25)
println(describe(john))
Variables & Definitions
Values & Variables
// Immutable (val)
val pi = 3.14
val numbers = List(1, 2, 3)
// Mutable (var)
var count = 0
count += 1
// Type annotations (optional)
val name: String = "Scala"
val age: Int = 30
Methods
// With explicit return type
def multiply(x: Int, y: Int): Int = {
x * y
}
// Single expression (no braces)
def square(x: Int) = x * x
// Multiple parameters lists
def fold[A, B](list: List[A])(z: B)(f: (B, A) => B): B = {
list.foldLeft(z)(f)
}
// Default parameters
def greet(name: String = "World") = s"Hello, $name"
// Named arguments
greet(name = "Scala")
Type Aliases
type UserId = String
type Result[T] = Either[String, T]
val id: UserId = "user-123"
val result: Result[Int] = Right(42)
Functions
Anonymous Functions
// Lambda syntax
val add = (x: Int, y: Int) => x + y
val square = (x: Int) => x * x
// Multi-line
val greet = (name: String) => {
val message = s"Hello, $name"
message.toUpperCase
}
// Type inference
List(1, 2, 3).map(x => x * 2)
Underscore Syntax
// Placeholder syntax
List(1, 2, 3).map(_ * 2)
List(1, 2, 3).filter(_ > 1)
// Multiple placeholders
val add = (_: Int) + (_: Int)
List(1, 2, 3).reduce(_ + _)
// With higher-order functions
val numbers = List(1, 2, 3)
numbers.map(_ * 2).filter(_ > 3)
Higher-Order Functions
// Function as parameter
def applyTwice(f: Int => Int, x: Int) = f(f(x))
applyTwice(_ * 2, 3) // 12
// Function as return value
def multiplier(factor: Int): Int => Int = {
(x: Int) => x * factor
}
val triple = multiplier(3)
triple(4) // 12
// Currying
def add(x: Int)(y: Int) = x + y
val add5 = add(5)_
add5(3) // 8
Collections
Common Collections
// List (immutable, linked list)
val list = List(1, 2, 3, 4)
val empty = List.empty[Int]
val prepend = 0 :: list
// Vector (immutable, indexed)
val vector = Vector(1, 2, 3, 4)
val updated = vector.updated(0, 10)
// Set (immutable, unique elements)
val set = Set(1, 2, 3, 2) // Set(1, 2, 3)
// Map (immutable, key-value)
val map = Map("a" -> 1, "b" -> 2)
val value = map.get("a") // Some(1)
Collection Operations
val numbers = List(1, 2, 3, 4, 5)
// map - transform elements
numbers.map(_ * 2) // List(2, 4, 6, 8, 10)
// filter - select elements
numbers.filter(_ > 2) // List(3, 4, 5)
// flatMap - map + flatten
numbers.flatMap(n => List(n, n * 10))
// List(1, 10, 2, 20, 3, 30, 4, 40, 5, 50)
// fold - accumulate from left
numbers.foldLeft(0)(_ + _) // 15
// reduce - accumulate without initial
numbers.reduce(_ + _) // 15
// collect - filter + map
numbers.collect {
case n if n % 2 == 0 => n * 2
} // List(4, 8)
Mutable Collections
import scala.collection.mutable
// Mutable List
val buffer = mutable.ListBuffer(1, 2, 3)
buffer += 4
buffer ++= List(5, 6)
// Mutable Set
val mutableSet = mutable.Set(1, 2, 3)
mutableSet += 4
// Mutable Map
val mutableMap = mutable.Map("a" -> 1)
mutableMap("b") = 2
mutableMap += ("c" -> 3)
Pattern Matching
Match Expressions
val number = 2
number match {
case 1 => "one"
case 2 => "two"
case 3 => "three"
case _ => "other"
}
// With guards
def describe(x: Int) = x match {
case n if n < 0 => "negative"
case 0 => "zero"
case n if n > 0 => "positive"
}
// Multiple conditions
def classify(x: Any) = x match {
case _: String => "string"
case _: Int => "integer"
case _ => "unknown"
}
Case Classes
// Define case class
case class Point(x: Int, y: Int)
case class Circle(center: Point, radius: Int)
// Auto-generated features:
// - Constructor parameters as fields
// - equals, hashCode, toString
// - copy method
// - companion object with apply
val p = Point(1, 2) // No 'new' needed
val p2 = p.copy(x = 5) // Point(5, 2)
// Pattern matching on case classes
def describe(shape: Any) = shape match {
case Point(0, 0) => "origin"
case Point(x, 0) => s"on x-axis at $x"
case Point(0, y) => s"on y-axis at $y"
case Point(x, y) => s"point at ($x, $y)"
case Circle(center, r) => s"circle at $center"
}
Sealed Traits (ADTs)
// Algebraic Data Types
sealed trait Result
case class Success(value: Int) extends Result
case class Failure(error: String) extends Result
case object Pending extends Result
// Exhaustive pattern matching
def handle(result: Result): String = result match {
case Success(v) => s"Got value: $v"
case Failure(e) => s"Error: $e"
case Pending => "Still processing..."
// Compiler warns if cases are missing
}
// Sealed hierarchies
sealed trait Tree
case class Leaf(value: Int) extends Tree
case class Branch(left: Tree, right: Tree) extends Tree
For Comprehensions
Basic Syntax
// Simple iteration
for (i <- 1 to 5) {
println(i)
}
// With yield (map)
val squares = for (i <- 1 to 5) yield i * i
// Vector(1, 4, 9, 16, 25)
// Multiple generators
for {
i <- 1 to 3
j <- 1 to 3
} yield (i, j)
// Vector((1,1), (1,2), (1,3), (2,1), ...)
Filters & Guards
// With filter
for {
i <- 1 to 10
if i % 2 == 0
} yield i
// Vector(2, 4, 6, 8, 10)
// Multiple filters
for {
i <- 1 to 20
if i % 2 == 0
if i % 3 == 0
} yield i
// Vector(6, 12, 18)
// Intermediate values
for {
i <- 1 to 5
square = i * i
if square > 10
} yield (i, square)
Desugaring to flatMap
// For comprehension
for {
x <- List(1, 2, 3)
y <- List(10, 20)
} yield x + y
// Desugars to:
List(1, 2, 3).flatMap { x =>
List(10, 20).map { y =>
x + y
}
}
Option & Try
Option (Null Safety)
// Some or None
val some: Option[Int] = Some(42)
val none: Option[Int] = None
// Pattern matching
def describe(opt: Option[Int]) = opt match {
case Some(value) => s"Got $value"
case None => "Got nothing"
}
// Common operations
some.getOrElse(0) // 42
none.getOrElse(0) // 0
some.map(_ * 2) // Some(84)
none.map(_ * 2) // None
some.filter(_ > 40) // Some(42)
some.filter(_ > 50) // None
// For comprehension
for {
a <- Some(10)
b <- Some(20)
} yield a + b // Some(30)
Try (Exception Handling)
import scala.util.{Try, Success, Failure}
// Wrapping unsafe code
def divide(a: Int, b: Int): Try[Int] = Try(a / b)
divide(10, 2) // Success(5)
divide(10, 0) // Failure(ArithmeticException)
// Pattern matching
divide(10, 0) match {
case Success(result) => s"Result: $result"
case Failure(exception) => s"Error: ${exception.getMessage}"
}
// Operations
Try("123".toInt).getOrElse(0) // 123
Try("abc".toInt).getOrElse(0) // 0
// Chaining
for {
a <- Try("10".toInt)
b <- Try("20".toInt)
} yield a + b // Success(30)
Either (Success or Error)
// Right (success) or Left (error)
def divide(a: Int, b: Int): Either[String, Int] = {
if (b == 0) Left("Division by zero")
else Right(a / b)
}
divide(10, 2) // Right(5)
divide(10, 0) // Left("Division by zero")
// Pattern matching
divide(10, 0) match {
case Right(result) => s"Result: $result"
case Left(error) => s"Error: $error"
}
Traits & Objects
Traits
// Define trait
trait Greeter {
def greet(name: String): String
}
// Implement trait
class EnglishGreeter extends Greeter {
def greet(name: String) = s"Hello, $name"
}
// Trait with implementation
trait Logger {
def log(message: String): Unit = {
println(s"[LOG] $message")
}
}
// Multiple traits (mixins)
class MyClass extends Greeter with Logger {
def greet(name: String) = {
log(s"Greeting $name")
s"Hello, $name"
}
}
Objects (Singletons)
// Singleton object
object Config {
val apiUrl = "https://api.example.com"
val timeout = 30
def isProduction = sys.env.get("ENV") == Some("prod")
}
// Usage
Config.apiUrl
// Companion object
case class User(id: String, name: String)
object User {
def create(name: String): User = {
User(java.util.UUID.randomUUID.toString, name)
}
}
val user = User.create("John")
Abstract Classes
// Abstract class
abstract class Animal {
def name: String
def sound: String
def makeSound() = println(s"$name says $sound")
}
// Concrete class
class Dog(val name: String) extends Animal {
def sound = "Woof!"
}
val dog = new Dog("Rex")
dog.makeSound() // Rex says Woof!
Implicits & Given-Using
Implicit Parameters (Scala 2)
// Define implicit value
implicit val defaultTimeout: Int = 30
// Method with implicit parameter
def fetchData(url: String)(implicit timeout: Int) = {
s"Fetching $url with timeout $timeout"
}
// Called without explicit parameter
fetchData("https://api.com") // Uses defaultTimeout
// Explicit call
fetchData("https://api.com")(60)
Given-Using (Scala 3)
// Define given instance
given defaultTimeout: Int = 30
// Method with using parameter
def fetchData(url: String)(using timeout: Int) = {
s"Fetching $url with timeout $timeout"
}
// Called without explicit parameter
fetchData("https://api.com")
Type Classes Pattern
// Type class
trait Serializer[A] {
def serialize(value: A): String
}
// Instances
given Serializer[Int] with {
def serialize(value: Int) = value.toString
}
given Serializer[String] with {
def serialize(value: String) = s""""$value""""
}
// Generic method using type class
def toJson[A](value: A)(using serializer: Serializer[A]): String = {
serializer.serialize(value)
}
toJson(42) // "42"
toJson("hello") // "\"hello\""
Futures & Async
Creating Futures
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
// Create future
val future = Future {
Thread.sleep(1000)
42
}
// Immediate values
val success = Future.successful(42)
val failure = Future.failed(new Exception("Error"))
Future Operations
// map - transform result
val doubled = future.map(_ * 2)
// flatMap - chain futures
val chained = future.flatMap { value =>
Future(value + 10)
}
// For comprehension
val combined = for {
a <- Future(10)
b <- Future(20)
c <- Future(30)
} yield a + b + c
// Handling completion
future.onComplete {
case Success(value) => println(s"Got $value")
case Failure(exception) => println(s"Failed: $exception")
}
Combining Futures
val f1 = Future(10)
val f2 = Future(20)
val f3 = Future(30)
// Sequence - List[Future] to Future[List]
Future.sequence(List(f1, f2, f3))
// Future(List(10, 20, 30))
// Traverse - map + sequence
val numbers = List(1, 2, 3)
Future.traverse(numbers)(n => Future(n * 2))
// Future(List(2, 4, 6))
// Zip - combine two futures
f1.zip(f2) // Future((10, 20))
Functional Patterns
Currying
// Curried function
def add(x: Int)(y: Int) = x + y
val add5 = add(5)_
add5(3) // 8
// Multiple parameter lists
def fold[A, B](list: List[A])(zero: B)(op: (B, A) => B): B = {
list.foldLeft(zero)(op)
}
val sum = fold(List(1, 2, 3))(0)(_ + _) // 6
Partial Application
def greet(greeting: String, name: String) = s"$greeting, $name"
val sayHello = greet("Hello", _: String)
sayHello("World") // Hello, World
// With multiple parameters
def process(a: Int, b: Int, c: Int) = a + b + c
val partial = process(10, _: Int, 30)
partial(20) // 60
Function Composition
// andThen
val addOne = (x: Int) => x + 1
val double = (x: Int) => x * 2
val addThenDouble = addOne andThen double
addThenDouble(3) // 8
// compose (reverse order)
val doubleFirst = addOne compose double
doubleFirst(3) // 7 (double then add)
Tail Recursion
import scala.annotation.tailrec
// Tail recursive function
@tailrec
def factorial(n: Int, acc: Int = 1): Int = {
if (n <= 1) acc
else factorial(n - 1, n * acc)
}
factorial(5) // 120
// Tail recursive sum
@tailrec
def sum(list: List[Int], acc: Int = 0): Int = list match {
case Nil => acc
case head :: tail => sum(tail, acc + head)
}
Common Gotchas
Mutable vs Immutable
// Immutable by default
val list = List(1, 2, 3)
// list += 4 // ERROR: reassignment to val
// Need mutable variant
import scala.collection.mutable
val buffer = mutable.ListBuffer(1, 2, 3)
buffer += 4 // OK
// Or use var with immutable collection
var immutableList = List(1, 2, 3)
immutableList = immutableList :+ 4 // Creates new list
Return Keyword
// Avoid explicit return
def add(a: Int, b: Int): Int = {
return a + b // BAD: unnecessary
}
// Prefer expression-oriented
def add(a: Int, b: Int): Int = a + b // GOOD
// Return exits early from lambda
List(1, 2, 3).map { x =>
return x * 2 // BAD: exits outer method!
}
// Use without return
List(1, 2, 3).map(x => x * 2) // GOOD
Null vs None
// Avoid null
var name: String = null // BAD
// Use Option instead
var name: Option[String] = None // GOOD
// Safe navigation
val length = name.map(_.length).getOrElse(0)
// Pattern matching
name match {
case Some(n) => s"Name is $n"
case None => "No name"
}
Equality
// Case classes compare by value
case class Person(name: String, age: Int)
val p1 = Person("John", 30)
val p2 = Person("John", 30)
p1 == p2 // true
// Regular classes compare by reference
class User(val name: String)
val u1 = new User("John")
val u2 = new User("John")
u1 == u2 // false (different instances)
Type System
Variance
// Covariant (use +)
trait Producer[+A] {
def produce(): A
}
// Contravariant (use -)
trait Consumer[-A] {
def consume(a: A): Unit
}
// Invariant (no annotation)
trait Box[A] {
def get: A
def set(a: A): Unit
}
// Example
class Animal
class Dog extends Animal
val dogProducer: Producer[Dog] = ???
val animalProducer: Producer[Animal] = dogProducer // OK
Type Bounds
// Upper bound (A must be subtype of Animal)
def process[A <: Animal](animal: A) = ???
// Lower bound (A must be supertype of Dog)
def add[A >: Dog](animal: A) = ???
// Context bound (requires implicit/given)
def sort[A: Ordering](list: List[A]) = list.sorted
// View bound (Scala 2.x, deprecated)
def print[A <% String](value: A) = println(value)
Generic Classes
// Simple generic
class Box[A](val value: A) {
def get: A = value
}
val intBox = new Box(42)
val stringBox = new Box("hello")
// Multiple type parameters
class Pair[A, B](val first: A, val second: B)
val pair = new Pair(1, "one")
Also see
- Scala Documentation - Official Scala documentation
- Scala 3 Book - Comprehensive Scala 3 guide
- Scala Exercises - Interactive exercises
- Scala Style Guide - Official style guide
- Awesome Scala - Curated list of Scala libraries
- GitHub Issue #196 - Original request