Scala
case classes Β· pattern match Β· FP Β· collections Β· Option/Either Β· Futures Β· Akka
Sheet 1 of 3
Scala 3
Intermediate
Printable
Scala Basics
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
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 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
// 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
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
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
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
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
| Symbol | Meaning |
|---|---|
| ! | tell β send message, no reply expected |
| ? | ask β returns Future[Any] with response |
| self | ActorRef to current actor |
| sender() | ActorRef of message sender |
| context | Actor's lifecycle & child management |
Traits & OOP
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
| Core Scala | Key point |
|---|---|
| Prefer immutable | val over var |
| Define case class | case class + copy |
| Sealed ADT | sealed trait + cases |
| Pattern match exhaustively | match + case _ |
| FP & Collections | Key point |
|---|---|
| Transform list | map / flatMap |
| Filter elements | filter / collect |
| Reduce to value | foldLeft(init)(fn) |
| Chain computations | for { } yield |
| Async & Safety | Key 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