scalaintermediate
For-Comprehensions and Monadic Composition
Use for-comprehensions with Option, Either, Future, and custom monads for elegant composition.
scalaPress ⌘/Ctrl + Shift + C to copy
import scala.concurrent.{Future, Await}
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration.*
import scala.util.{Try, Success, Failure}
case class User(id: Int, name: String, departmentId: Int)
case class Department(id: Int, name: String, managerId: Int)
def findUser(id: Int): Option[User] =
Map(1 -> User(1, "Alice", 10), 2 -> User(2, "Bob", 20)).get(id)
def findDept(id: Int): Option[Department] =
Map(10 -> Department(10, "Engineering", 1)).get(id)
def findManager(id: Int): Option[User] = findUser(id)
@main def run(): Unit =
// Option for-comprehension
val managerName = for
user <- findUser(1)
dept <- findDept(user.departmentId)
manager <- findManager(dept.managerId)
yield manager.name
println(s"Manager: ${managerName.getOrElse("unknown")}")
// Fails gracefully at any step
val missing = for
user <- findUser(99)
dept <- findDept(user.departmentId)
manager <- findManager(dept.managerId)
yield manager.name
println(s"Missing: $missing") // None
// For-comprehension with filter (guard)
val adults = for
age <- List(15, 22, 17, 30, 12, 25)
if age >= 18
yield age
println(s"Adults: $adults")
// Nested iteration
val pairs = for
x <- 1 to 3
y <- 1 to 3
if x != y
yield (x, y)
println(s"Pairs: ${pairs.toList}")
// Either for-comprehension
def parseInt(s: String): Either[String, Int] =
Try(s.toInt).toEither.left.map(_ => s"'$s' is not a number")
def validateAge(age: Int): Either[String, Int] =
if age >= 0 && age <= 150 then Right(age)
else Left(s"Age $age out of range")
val validated = for
num <- parseInt("25")
age <- validateAge(num)
yield s"Valid age: $age"
println(validated) // Right(Valid age: 25)
val invalid = for
num <- parseInt("abc")
age <- validateAge(num)
yield s"Valid age: $age"
println(invalid) // Left('abc' is not a number)
// Future for-comprehension
def fetchUser(): Future[String] = Future { Thread.sleep(50); "Alice" }
def fetchOrders(user: String): Future[List[String]] =
Future { Thread.sleep(50); List("Order1", "Order2") }
val ordersFuture = for
user <- fetchUser()
orders <- fetchOrders(user)
yield s"$user has ${orders.size} orders"
println(Await.result(ordersFuture, 5.seconds))Use Cases
- Chaining optional computations
- Validation pipelines with Either
- Async workflow composition with Future
Tags
Related Snippets
Similar patterns you can reuse in the same workflow.
scalabeginner
Collections Map Filter Fold Operations
Master Scala collections: map, flatMap, filter, fold, groupBy, partition, and zip operations.
Best for: Data transformation and aggregation
#scala#collections
scalaadvanced
Type Class Pattern Implementation
Implement the type class pattern in Scala 3: define, provide instances, and use with extension methods.
Best for: Ad-hoc polymorphism without inheritance
#scala#type-class
scalaadvanced
Cats Effect IO Monad Basics
Use Cats Effect IO for pure functional effects: sequencing, error handling, resource management.
Best for: Pure functional effect management
#scala#cats-effect
scalaadvanced
ZIO Effect System Basics
Build programs with ZIO: effects, error handling, layers, and concurrent operations.
Best for: Typed error handling with ZIO
#scala#zio