Methods and function values

Scala · Essentials · 12 min

Functions as values are the basis of the collections API and of functional style.

A method is defined with def: def area(w: Double, h: Double = 1.0): Double = w * h. Parameter types are required, the result type is usually inferred but should be written on public methods, and the body is a single expression (or an indented block). Callers can use named arguments in any order (area(h = 2, w = 3)) and skip parameters with defaults.

Methods can have several parameter lists, def add(a: Int)(b: Int) = a + b, useful for type inference, using clauses and passing blocks: retry(3) { ... }. A parameter typed => A is by-name: evaluated each time it's used rather than before the call, which lets you write control-structure-like methods.

A function value (lambda) is written (x: Int) => x * 2. When the expected type is known you can drop the types, xs.map(x => x * 2), or use the placeholder shorthand xs.map(_ * 2). Function types read naturally: Int => Int, (String, Int) => Boolean.

Methods are converted to functions automatically when a function is expected (eta-expansion), so xs.map(area(_, 2)) or names.foreach(println) just work.

Varargs use *: def sum(xs: Int*) = xs.sum, and a sequence is passed with sum(list*).

Example

def area(w: Double, h: Double = 1.0): Double = w * h
println(area(3))                        // 3.0
println(area(h = 2, w = 5))             // 10.0

val double: Int => Int = x => x * 2
val add = (a: Int, b: Int) => a + b
println(List(1, 2, 3).map(double))      // List(2, 4, 6)
println(List(1, 2, 3).map(_ + 10))      // List(11, 12, 13)
println(List(4, 5).reduce(add))         // 9

def twice(f: Int => Int, x: Int): Int = f(f(x))
println(twice(_ * 3, 2))                // 18

def logIf(enabled: Boolean)(msg: => String): Unit =
  if enabled then println(msg)
logIf(false) { println("never evaluated"); "expensive" }
logIf(true)("cheap")                    // cheap

def sum(xs: Int*): Int = xs.sum
println(sum(1, 2, 3) + sum(List(4, 5)*))  // 15

Remember

  • def defines methods; named and default arguments make calls clear
  • Lambdas: x => x * 2 or the placeholder _ * 2
  • => A parameters are by-name, evaluated only when used
  • Methods turn into functions automatically when needed

Watch out for

  • Using _ twice expecting the same argument: _ * _ is a two-argument function.

Check yourself

  1. What does List(1, 2).map(_ * _) do?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.scala-lang.org/scala3/book/methods-most.html