Scala Cheat Sheet β€” Case Classes, Pattern Matching, FP, Akka | Dataplexa

Scala

case classes  Β·  pattern match  Β·  FP  Β·  collections  Β·  Option/Either  Β·  Futures  Β·  Akka

Sheet 1 of 3 Scala 3 Intermediate Printable

Scala Basics

must know first
Variables & Types
// val β€” immutable (preferred)
val name: String = "Alice"
val age  = 25          // inferred Int

// var β€” mutable (avoid when possible)
var count: Int = 0
count += 1

// Scala 3 β€” no semicolons needed
val pi   = 3.14159     // Double
val ok   = true        // Boolean
val ch   = 'A'         // Char

// String interpolation
println(s"Hello, $name! Age: $age")
println(f"Pi = $pi%.2f")
Functions & Methods
// def β€” named function
def add(a: Int, b: Int): Int = a + b

// Single-expression β€” no braces
def square(x: Int) = x * x

// Default parameters
def greet(name: String = "World") =
  println(s"Hi $name")

// Lambda / anonymous function
val double = (x: Int) => x * 2
double(5)  // 10

// Higher-order function
def apply(f: Int => Int, x: Int) = f(x)
apply(double, 4)  // 8
Control Flow
// if/else is an expression
val result = if (age >= 18) "adult"
              else            "minor"

// for comprehension
for (i <- 1 to 5)
  println(i)

// for with guard (filter)
for (i <- 1 to 10 if i % 2 == 0)
  println(i)  // 2 4 6 8 10

// while loop
var n = 0
while (n < 3) { println(n); n += 1 }
val over var: Scala encourages immutability. Use val by default β€” it makes code easier to reason about and is thread-safe. Only use var when mutation is truly needed.

Case Classes

immutable data Β· equals Β· copy
Define & Use
// case class β€” auto-generates:
// equals, hashCode, toString, copy, apply
case class Person(name: String, age: Int)

// No `new` needed
val alice = Person("Alice", 30)
val bob   = Person("Bob",   25)

// Access fields
alice.name    // "Alice"
alice.age     // 30

// Equality by value
Person("A",1) == Person("A",1)  // true

// copy β€” modify a field
val older = alice.copy(age = 31)
// Person("Alice", 31)
Sealed Trait + ADT
// Algebraic Data Type (ADT)
sealed trait Shape
case class Circle(r: Double)          extends Shape
case class Rect(w: Double, h: Double) extends Shape
case object Point                     extends Shape

// sealed = compiler knows all subtypes
// β†’ exhaustiveness checking in match
sealed trait + case class is Scala's pattern for sum types. The compiler warns you if a pattern match is non-exhaustive.

Pattern Matching

match Β· guards Β· destructuring
match Expression
val x: Any = 42

val desc = x match {
  case 0                => "zero"
  case n: Int if n>0  => s"positive: $n"
  case n: Int           => s"negative: $n"
  case s: String        => s"string: $s"
  case _                => "other"
}
Case Class Destructuring
def area(s: Shape): Double = s match {
  case Circle(r)    => 3.14159 * r * r
  case Rect(w, h) => w * h
  case Point        => 0.0
}

// Tuple destructuring
val pair = (1, "hello")
pair match {
  case (n, s) => println(s"$n : $s")
}

// List pattern
def head(lst: List[Int]) = lst match {
  case Nil          => "empty"
  case h :: Nil    => s"one: $h"
  case h :: t      => s"head=$h rest=${t.length}"
}

Collections

List Β· Vector Β· Map Β· Set Β· immutable by default
List & Vector
// List β€” linked list, immutable
val nums = List(1,2,3,4,5)
nums.head         // 1
nums.tail         // List(2,3,4,5)
nums.isEmpty      // false
0 :: nums         // prepend β†’ List(0,1...)
nums ::: List(6,7) // concat

// Vector β€” indexed, O(log n) update
val v = Vector(1,2,3)
v(0)              // 1
v.updated(1, 99) // Vector(1,99,3)
v :+ 4           // append
0 +: v           // prepend
Map & Set
// Map β€” immutable key-value
val m = Map("a" -> 1, "b" -> 2)
m("a")           // 1 (throws if missing)
m.get("a")       // Some(1)
m.getOrElse("x",0) // 0
m + ("c" -> 3)  // add entry
m - "a"          // remove key
m.keys; m.values

// Set β€” unique elements
val s = Set(1,2,3,2)  // Set(1,2,3)
s.contains(2)    // true
s + 4            // add element
s - 1            // remove
s ++ Set(5,6)   // union
Higher-Order Collection Ops
val nums = List(1,2,3,4,5)

// map β€” transform each element
nums.map(_ * 2)    // List(2,4,6,8,10)

// filter β€” keep matching
nums.filter(_ % 2 == 0)  // List(2,4)

// foldLeft β€” reduce with accumulator
nums.foldLeft(0)(_ + _)   // 15 (sum)

// flatMap β€” map then flatten
List(1,2,3).flatMap(x =>
  List(x, x*10))    // List(1,10,2,20,3,30)

nums.take(3)       // List(1,2,3)
nums.drop(3)       // List(4,5)
nums.sorted        // sorted ascending
nums.reverse       // List(5,4,3,2,1)
Immutable by default: All Scala collection ops return new collections β€” originals are never modified. Use scala.collection.mutable._ when you explicitly need mutation.

Option & Either

null-safe Β· error handling
Option β€” Some or None
// Option replaces null
val a: Option[Int] = Some(42)
val b: Option[Int] = None

a.getOrElse(0)  // 42
b.getOrElse(0)  // 0

a.map(_ * 2)    // Some(84)
b.map(_ * 2)    // None (no NPE!)

// Pattern match on Option
a match {
  case Some(v) => println(s"Got $v")
  case None    => println("nothing")
}
Either β€” Right (success) or Left (error)
def divide(a:Int, b:Int):
    Either[String,Double] =
  if (b == 0) Left("division by zero")
  else         Right(a.toDouble / b)

divide(10,2) match {
  case Right(v)  => println(s"= $v")
  case Left(err) => println(s"Error: $err")
}

// map only applies on Right
divide(10,2).map(_ * 100)  // Right(500.0)
Convention: Right = success value, Left = error value. Use Either instead of throwing exceptions in functional Scala code.

Functional Programming

pure functions Β· composition Β· for-comprehension
Function Composition & Currying
// compose  f compose g  = f(g(x))
val double    = (x:Int) => x * 2
val addOne    = (x:Int) => x + 1
val doubleThenAdd = addOne.compose(double)
doubleThenAdd(5)   // 11

// andThen  f andThen g  = g(f(x))
val pipeline = double.andThen(addOne)
pipeline(5)         // 11

// Currying
def add(a:Int)(b:Int) = a + b
val add5 = add(5) _
add5(3)              // 8
for-comprehension (monadic)
// Works on Option, Either, Future, List
val result = for {
  x <- Some(10)
  y <- Some(5)
} yield x + y
// Some(15)

// With None β€” short circuits
val r2 = for {
  x <- Some(10)
  y <- None: Option[Int]
} yield x + y
// None β€” safe, no exception

// List comprehension
val pairs = for {
  x <- List(1,2)
  y <- List("a","b")
} yield (x, y)
// List((1,a),(1,b),(2,a),(2,b))

Futures & Async

concurrent Β· non-blocking Β· ExecutionContext
Create & Compose
import scala.concurrent._
import ExecutionContext.Implicits.global

// Create a future
val f = Future {
  Thread.sleep(100)
  42
}

// map β€” transform result
val doubled = f.map(_ * 2)

// flatMap β€” chain futures
val chained = f.flatMap(n =>
  Future { n + 1 }
)

// recover β€” handle failure
val safe = f.recover {
  case e: Exception => -1
}
onComplete & Await
import scala.util.{Success,Failure}

// onComplete β€” callback (non-blocking)
f.onComplete {
  case Success(v)  => println(s"Got $v")
  case Failure(ex) => println(s"Error: $ex")
}

// Await (blocking β€” avoid in production)
import scala.concurrent.Await
import scala.concurrent.duration._
val result = Await.result(f, 5.seconds)
Future.sequence & for-comprehension
// Run futures in parallel, collect
val fs = List(
  Future{1},
  Future{2},
  Future{3}
)
Future.sequence(fs)
// Future(List(1,2,3))

// for-comprehension on Future
val combined = for {
  a <- Future{10}
  b <- Future{20}
} yield a + b
// Future(30)

Akka Actor Model

actors Β· messages Β· ActorSystem
Classic Akka Actors
import akka.actor.{Actor,ActorSystem,Props}

// Define messages
case class Greet(name: String)
case object Stop

// Define actor
class Greeter extends Actor {
  def receive = {
    case Greet(n) => println(s"Hello $n")
    case Stop    => context.stop(self)
  }
}

// Create and use
val sys = ActorSystem("MySystem")
val ref = sys.actorOf(Props[Greeter])
ref ! Greet("Alice")   // ! = tell (fire and forget)
ref ! Stop
SymbolMeaning
! tell β€” send message, no reply expected
? ask β€” returns Future[Any] with response
selfActorRef to current actor
sender()ActorRef of message sender
contextActor's lifecycle & child management

Traits & OOP

traits Β· inheritance Β· generics
Traits (like interfaces + mixins)
trait Greetable {
  def greet(): String
  def hello() = println(greet())  // concrete
}

trait Farewell {
  def bye() = println("Goodbye!")
}

class Person(val name: String)
    extends Greetable
    with    Farewell {
  def greet() = s"Hi, I'm $name"
}

val p = new Person("Alice")
p.hello()   // "Hi, I'm Alice"
p.bye()     // "Goodbye!"
Traits vs abstract classes: Prefer traits for mixins. Use abstract classes when you need constructor parameters or Java interop.

Scala Mastery Checklist

sheet 1 complete
Core ScalaKey point
Prefer immutable val over var
Define case class case class + copy
Sealed ADT sealed trait + cases
Pattern match exhaustivelymatch + case _
FP & CollectionsKey point
Transform list map / flatMap
Filter elements filter / collect
Reduce to value foldLeft(init)(fn)
Chain computationsfor { } yield
Async & SafetyKey point
Null-safe value Option[T]
Error handling Either[E, A]
Async computation Future { block }
Actor message actor ! Message
Next up β†’ Sheet 2: Dart  Β·  null safety Β· async/await Β· futures Β· Flutter widgets Β· streams Β· class syntax