0Pricing
Scala for Backend Engineering & Functional Programming · Lesson

Closures

Capturing state.

Closures is a free Scala for Backend Engineering & Functional Programming lesson on CoddyKit — lesson 4 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Scala for Backend Engineering & Functional Programming learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

What is a closure?

A closure is a function that captures variables from the surrounding scope where it was defined. It 'closes over' those variables, keeping access to them even after that scope has returned.

object Main {
  def main(args: Array[String]): Unit = {
    val factor = 3
    val multiply = (x: Int) => x * factor
    println(multiply(5))
  }
}

Capturing a free variable

In x => x * factor, x is a parameter but factor is a free variable taken from the enclosing scope. The function carries a reference to it.

object Main {
  def main(args: Array[String]): Unit = {
    val greeting = "Hello"
    val greet = (name: String) => s"$greeting, $name!"
    println(greet("Ann"))
    println(greet("Bob"))
  }
}

Closures outlive their scope

When a function returns a closure, the captured variables stay alive as long as the closure does, even though the defining function has already finished.

object Main {
  def makeAdder(n: Int): Int => Int = x => x + n
  def main(args: Array[String]): Unit = {
    val add10 = makeAdder(10)
    val add100 = makeAdder(100)
    println(add10(5))
    println(add100(5))
  }
}

Each closure has its own capture

Calling a function factory twice produces two independent closures, each remembering its own captured value.

object Main {
  def prefixer(p: String): String => String = s => p + s
  def main(args: Array[String]): Unit = {
    val withDr = prefixer("Dr. ")
    val withMr = prefixer("Mr. ")
    println(withDr("Smith"))
    println(withMr("Jones"))
  }
}

Capturing mutable state

A closure can capture a var and both read and update it. This lets a closure maintain state between calls, like a counter.

object Main {
  def main(args: Array[String]): Unit = {
    var count = 0
    val increment = () => { count += 1; count }
    println(increment())
    println(increment())
    println(increment())
  }
}

A counter factory

Combine the ideas: a function that returns a closure capturing its own private var gives you an encapsulated, independent counter.

object Main {
  def counter(): () => Int = {
    var n = 0
    () => { n += 1; n }
  }
  def main(args: Array[String]): Unit = {
    val c1 = counter()
    val c2 = counter()
    println(c1())
    println(c1())
    println(c2())
  }
}

Closures capture references, not copies

A closure captures the variable, not a snapshot. If the captured var changes after the closure is created, the closure sees the new value.

object Main {
  def main(args: Array[String]): Unit = {
    var msg = "first"
    val show = () => println(msg)
    show()
    msg = "second"
    show()
  }
}

Prefer capturing val

Capturing an immutable val avoids surprises: the captured value never changes, so the closure behaves predictably. Favor val capture in functional code.

object Main {
  def main(args: Array[String]): Unit = {
    val taxRate = 0.2
    val withTax = (price: Double) => price * (1 + taxRate)
    println(withTax(100))
  }
}

Closures with collection methods

Lambdas passed to map, filter, etc. are closures whenever they reference outside variables. This is extremely common in everyday Scala.

object Main {
  def main(args: Array[String]): Unit = {
    val threshold = 3
    val nums = List(1, 2, 3, 4, 5)
    val big = nums.filter(_ > threshold)
    println(big)
  }
}

A capture pitfall in loops

In Scala, a for loop variable is a fresh binding each iteration, so closures capture distinct values. This avoids a classic bug seen in some other languages.

object Main {
  def main(args: Array[String]): Unit = {
    val fns = for (i <- 1 to 3) yield () => i
    println(fns.map(f => f()))
  }
}

Closures encapsulate behavior + data

A closure bundles behavior (the function body) with data (the captured variables). This is a lightweight alternative to a small object with one method.

object Main {
  def discounter(rate: Double): Double => Double = price => price * (1 - rate)
  def main(args: Array[String]): Unit = {
    val sale = discounter(0.25)
    println(sale(80))
    println(sale(200))
  }
}

Quick Check

What does a closure capture from its surrounding scope?

Recap

You learned about closures:

  • A closure captures free variables from its enclosing scope
  • Captured variables outlive the defining function
  • Each closure instance has its own captured state
  • Closures capture references, so mutable captures can change
  • Lambdas to map/filter are everyday closures

Frequently asked questions

Is the “Closures” lesson free?

Yes — the full text of “Closures” is free to read here on the web, and the Scala for Backend Engineering & Functional Programming course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Scala for Backend Engineering & Functional Programming course, upgrade to CoddyKit PRO.

What will I learn in “Closures”?

Capturing state. You practise Scala for Backend Engineering & Functional Programming with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start Scala for Backend Engineering & Functional Programming?

No prior experience is required. Scala for Backend Engineering & Functional Programming on CoddyKit is structured for beginners through advanced learners; this is — lesson 4 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “Closures” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this Scala for Backend Engineering & Functional Programming lesson?

Yes. Every Scala for Backend Engineering & Functional Programming lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. Functions as Values
  2. Currying
  3. Composition
  4. Closures
← Back to Scala for Backend Engineering & Functional Programming