Scala Functions: def, Lambda, and Higher-Order Functions

TT
TopicTrick

Functions are first-class citizens in Scala — they can be passed as arguments, returned from other functions, stored in variables, and composed together. This makes Scala's functional programming capabilities qualitatively different from Java, where lambdas are a bolted-on addition. Understanding how Scala handles functions is fundamental to writing idiomatic code.

Defining Functions with def

The basic function definition:

scala
def greet(name: String): String =
  s"Hello, $name!"

greet("Alice")  // "Hello, Alice!"

Multi-line functions use a block expression:

scala
def max(a: Int, b: Int): Int =
  if a > b then a else b

The last expression in a function body is its return value — no return keyword needed (though it exists for early returns).

Functions with default parameters:

scala
def connect(host: String, port: Int = 8080): String =
  s"Connecting to $host:$port"

connect("localhost")        // "Connecting to localhost:8080"
connect("example.com", 443) // "Connecting to example.com:443"

Named arguments:

scala
connect(port = 9000, host = "db.server")

Anonymous Functions (Lambdas)

Anonymous functions are function literals — functions without a name:

scala
val double = (x: Int) => x * 2
double(5)  // 10

val add = (a: Int, b: Int) => a + b
add(3, 4)  // 7

When the type can be inferred from context, you can omit it:

scala
val numbers = List(1, 2, 3, 4, 5)
numbers.map(x => x * 2)   // List(2, 4, 6, 8, 10)

Placeholder syntax_ as shorthand for a single parameter used once:

scala
numbers.map(_ * 2)        // same as x => x * 2
numbers.filter(_ > 3)     // same as x => x > 3
numbers.reduce(_ + _)     // same as (a, b) => a + b

Higher-Order Functions

A higher-order function takes another function as a parameter or returns a function:

scala
def applyTwice(f: Int => Int, x: Int): Int =
  f(f(x))

applyTwice(_ * 2, 3)   // 12  (3 * 2 = 6, 6 * 2 = 12)
applyTwice(_ + 10, 5)  // 25

The type Int => Int represents a function from Int to Int. For two parameters: (Int, Int) => Int.

Returning a function:

scala
def multiplier(factor: Int): Int => Int =
  (x: Int) => x * factor

val triple = multiplier(3)
triple(7)  // 21

val times10 = multiplier(10)
times10(4) // 40

Function Values vs Methods

There is a subtle distinction between def methods and function values:

scala
def add(a: Int, b: Int): Int = a + b   // method — belongs to a class
val addFn: (Int, Int) => Int = add     // function value — eta-expansion

When you use a def where a function value is expected, Scala automatically performs eta-expansion — converting the method to a function value.

Currying

Currying transforms a function with multiple parameters into a chain of single-parameter functions:

scala
def add(a: Int)(b: Int): Int = a + b

val add5 = add(5)  // partially applied — returns Int => Int
add5(3)            // 8
add5(10)           // 15

Multiple parameter lists in Scala enable currying. The add(5) call returns a new function that adds 5 to its argument.

Practical use — custom control structures:

scala
def repeat(n: Int)(block: => Unit): Unit =
  var i = 0
  while i < n do
    block
    i += 1

repeat(3):
  println("Hello!")
// Hello!
// Hello!
// Hello!

Partial Application

Partial application fixes some arguments of a function, producing a new function:

scala
def power(base: Int, exp: Int): Int =
  Math.pow(base, exp).toInt

val square = power(_, 2)    // fix exp=2, base is free
val cube   = power(_, 3)

square(5)  // 25
cube(4)    // 64

The _ placeholder marks the free (unfixed) argument.

Frequently Asked Questions

Q: What is the difference between => and -> in Scala? => is the function arrow — used in function types (Int => String) and lambda bodies (x => x * 2). -> creates a tuple pair, commonly used in Map literals: Map("key" -> "value"). They are unrelated operators. -> desugars to ("key", "value").

Q: When should I use def vs val for a function? Use def for methods on classes and for functions that should be computed each time they are called (or that have side effects). Use val for function values you want to store, pass around, or partially apply. In practice, def is the default choice and val for a function is used when you are explicitly treating the function as a value.

Q: What is the => in a parameter type like block: => Unit? => Unit (with no left side) is a by-name parameter — the argument is not evaluated when passed, but each time it is used in the function body. This is how Scala implements custom control structures: passing a block of code that executes later. It is different from () => Unit (a zero-argument function that must be explicitly called with ()).


Part of the Scala Mastery Course — Module 5 of 22.