scalaintermediate

Partial Functions and Composition

Use PartialFunction for handlers, pattern matching functions, and safe composition with orElse/andThen.

scala
// PartialFunction: defined only for some inputs
val reciprocal: PartialFunction[Double, Double] =
  case d if d != 0 => 1.0 / d

val intToString: PartialFunction[Int, String] =
  case 1 => "one"
  case 2 => "two"
  case 3 => "three"

// Check if defined
println(reciprocal.isDefinedAt(5))   // true
println(reciprocal.isDefinedAt(0))   // false

// Composition with orElse
val handleSmall: PartialFunction[Int, String] =
  case n if n < 10 => s"small($n)"

val handleMedium: PartialFunction[Int, String] =
  case n if n < 100 => s"medium($n)"

val handleLarge: PartialFunction[Int, String] =
  case n => s"large($n)"

val handleAll = handleSmall orElse handleMedium orElse handleLarge

// Event handler pattern
sealed trait Event
case class Click(x: Int, y: Int) extends Event
case class KeyPress(key: Char) extends Event
case class Scroll(delta: Int) extends Event
case class Resize(w: Int, h: Int) extends Event

val clickHandler: PartialFunction[Event, String] =
  case Click(x, y) => s"Clicked at ($x, $y)"

val keyHandler: PartialFunction[Event, String] =
  case KeyPress(k) => s"Key pressed: $k"

val scrollHandler: PartialFunction[Event, String] =
  case Scroll(d) if d > 0 => "Scrolled up"
  case Scroll(d) if d < 0 => "Scrolled down"
  case Scroll(0) => "No scroll"

val defaultHandler: PartialFunction[Event, String] =
  case e => s"Unhandled: $e"

val eventHandler = clickHandler orElse keyHandler orElse scrollHandler orElse defaultHandler

// With collect (filter + map using PartialFunction)
val data: List[Any] = List(1, "hello", 2, true, 3, "world", 4.5)

val strings = data.collect { case s: String => s.toUpperCase }
val ints = data.collect { case i: Int => i * 10 }

// andThen composition
val doubler: PartialFunction[Int, Int] =
  case n if n > 0 => n * 2

val toString_ = doubler.andThen(n => s"result=$n")

// Lift to Option
val safeLookup = intToString.lift

@main def run(): Unit =
  println(s"Handle 5: ${handleAll(5)}")
  println(s"Handle 50: ${handleAll(50)}")
  println(s"Handle 500: ${handleAll(500)}")

  println()
  val events: List[Event] = List(
    Click(100, 200),
    KeyPress('a'),
    Scroll(-3),
    Resize(800, 600)
  )
  events.foreach(e => println(s"  ${eventHandler(e)}"))

  println(s"\nStrings: $strings")
  println(s"Ints: $ints")

  println(s"\nLifted 1: ${safeLookup(1)}")
  println(s"Lifted 5: ${safeLookup(5)}")

  println(s"\ncomposed: ${toString_(5)}")
  // toString_(0) would throw MatchError
  println(s"Is defined at 0: ${toString_.isDefinedAt(0)}")

Use Cases

  • Event handling systems
  • Safe pattern matching
  • Composable request handlers

Tags

Related Snippets

Similar patterns you can reuse in the same workflow.