Scala Functional Programming

Scala's functional programming features let you write expressive, composable, and testable code. Functions are first-class values that can be passed, returned, and combined.

Functions as Values

Scala
// Function type: (input) => output
val double: Int => Int = _ * 2
val greet: String => String = name => s"Hello, $name!"

// Higher-order functions
def applyToAll[A, B](list: List[A], f: A => B): List[B] = list.map(f)

applyToAll(List(1, 2, 3), double)      // List(2, 4, 6)
applyToAll(List("a", "b"), _.toUpperCase)  // List("A", "B")

// Partial application
def add(a: Int, b: Int): Int = a + b
val add5 = add(5, _: Int)  // Int => Int
add5(3)  // 8

// Currying
def multiply(a: Int)(b: Int): Int = a * b
val triple = multiply(3) _  // Int => Int
triple(4)  // 12

Immutability Patterns

Scala
// Immutable data structures
val list = List(1, 2, 3)
val newList = list :+ 4           // List(1, 2, 3, 4)
val filtered = list.filter(_ > 1) // List(2, 3)

// Builder pattern with immutable state
case class Config(
  host: String = "localhost",
  port: Int = 8080,
  debug: Boolean = false,
  timeout: Int = 30
)

val productionConfig = Config(host = "prod.example.com", port = 443)
val debugConfig = productionConfig.copy(debug = true, port = 8080)

// Fold to transform data
val words = List("hello", "world", "scala")
val lengthMap = words.foldLeft(Map.empty[String, Int]) { (map, word) =>
  map + (word -> word.length)
}
// Map("hello" -> 5, "world" -> 5, "scala" -> 5)

Composing Functions

Scala
// Function composition
val trim: String => String = _.trim
val lowercase: String => String = _.toLowerCase
val removeSpaces: String => String = _.replaceAll("\\s", "-")

// Compose: (f ∘ g)(x) = f(g(x))
val normalize = trim andThen lowercase andThen removeSpaces
normalize("  Hello World  ")  // "hello-world"

// Pipe operator style
def pipe2[A, B, C](f: A => B, g: B => C): A => C = f andThen g

// Lift: wrap a function to work on Option
def lift[A, B](f: A => B): Option[A] => Option[B] = _.map(f)

val safeDouble: Option[Int] => Option[Int] = lift((x: Int) => x * 2)
safeDouble(Some(5))   // Some(10)
safeDouble(None)      // None

For Comprehensions (Desugared)

Scala
// 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))
// List(11, 12, 21, 22, 31, 32)

// With guards
for {
  x <- List(1, 2, 3, 4, 5)
  if x % 2 == 0
} yield x * x
// List(4, 16)

// Chained for-comprehensions with Option
def lookupUser(id: Int): Option[User] = ???
def getOrders(user: User): Option[List[Order]] = ???
def getTotal(order: Order): Option[Double] = ???

val total: Option[Double] = for {
  user <- lookupUser(1)
  orders <- getOrders(user)
  order <- orders.headOption
  total <- getTotal(order)
} yield total

Monads in Scala

Scala
// Option, List, Try, Either, Future are all monads
// They share: flatMap, map, filter, fold

// Option monad (handles nullability)
val result: Option[Int] = for {
  a <- Some(10)
  b <- Some(5)
} yield a - b
// Some(5)

// Either monad (handles errors)
def parse(s: String): Either[String, Int] = 
  Either.cond(s.forall(_.isDigit), s.toInt, s"$s is not a number")

val sum: Either[String, Int] = for {
  a <- parse("10")
  b <- parse("5")
} yield a + b
// Right(15)

// Future monad (handles async)
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global

val userData: Future[User] = for {
  user <- fetchUser(id)
  orders <- fetchOrders(user.id)
} yield user.copy(recentOrders = orders)

Tail Recursion

Scala
import scala.annotation.tailrec

// Tail-recursive factorial
@tailrec
def factorial(n: Int, acc: Int = 1): Int = 
  if (n <= 1) acc
  else factorial(n - 1, n * acc)

factorial(10)  // 3628800

// Tail-recursive Fibonacci
@tailrec
def fibonacci(n: Int, a: Int = 0, b: Int = 1): Int = 
  if (n == 0) a
  else fibonacci(n - 1, b, a + b)

fibonacci(10)  // 55

// Tail-recursive list processing
@tailrec
def sumList(list: List[Int], acc: Int = 0): Int = list match {
  case Nil => acc
  case head :: tail => sumList(tail, acc + head)
}

💡 Tip: Use .foldLeft or .foldRight instead of var loops. They're safer (no state bugs), composable, and often more readable.

Next: Pattern Matching and Futures