Week 4 Code Snippets (SP26)

Author: Stefan Mitsch

Based on the code snippets developed in class, summaries and explanations in this document were drafted with the assistance of generative AI. The author verified all facts, revised the text for coherence, and takes full responsibility for the final content.

Week 4 Overview

Week 4 demonstrated methods/functions and currying, folds, tail recursion. Explanations add context from static vs. dynamic typing and the prior functional-programming lecture.

1. From Methods to Functions

A good starting point is to compare a method definition in an object with a function value.

object Methods:
  val x = 5
  def add(y: Int, z: Int) = x + y + z
end Methods

The method add is part of object structure and can use object fields (x). Now compare that with a function value:

val add: (Int, Int) => Int =
  (x: Int, y: Int) => x + y

val add2: (Int, Int) => Int =
  (_: Int) + (_: Int)

This difference is emphasized in the currying slides: methods organize behavior in class/object structure, while functions are first-class values that can be passed, returned, and stored.

Scala 3 Book pointers: - Methods: https://docs.scala-lang.org/scala3/book/methods-main-methods.html - Functions: https://docs.scala-lang.org/scala3/book/fun-intro.html - Anonymous functions: https://docs.scala-lang.org/scala3/book/fun-anonymous-functions.html

Practical relevance: In larger systems, methods are useful for stable APIs, while function values make extension points easy (custom sorting, callbacks, validation pipelines, strategy selection).

2. Higher-Order Functions: Passing Behavior

Once functions are values, we can pass behavior into generic code:

def g(x: Int, y: Int, f: (Int, Int) => Int) =
  f(x, y)
end g

g(1, 2, _ + _)
g(1, 2, (x, y) => x + y)

We can also return functions from functions:

def createAdd(): (Int, Int) => Int =
  val result: (Int, Int) => Int =
    (x: Int, y: Int) => x + y
  result
end createAdd

val sum: Int = createAdd()(3, 4)

This is the core idea of higher-order programming from the functional-programming and currying lectures: separate traversal or orchestration logic from the specific operation.

Scala 3 Book pointers: - Higher-order functions: https://docs.scala-lang.org/scala3/book/fun-hofs.html

Practical relevance: This style scales well in production code because shared infrastructure can remain unchanged while business rules are injected as function arguments.

3. Currying and Partial Application

Next, the snippet introduces a curried version of addition:

val addCurried: Int => Int => Int =
  (x: Int) =>
    (y: Int) =>
      x + y

val sum2: Int = addCurried(1)(2)

With collections, this enables partial application:

List(1, 2, 3).map(x => x + 1)
List(1, 2, 3).map(add(_, 1))
List(1, 2, 3).map(addCurried(1))

The demonstrated insights from the slides are: tupled multi-argument style and curried single-argument-chain style are both useful, and partial application creates specialized functions from general ones.

Scala 3 Book pointers: - Eta expansion and partially applied functions: https://docs.scala-lang.org/scala3/book/fun-eta-expansion.html - Methods and functions in practice: https://docs.scala-lang.org/scala3/book/methods-main-methods.html

Practical relevance: In larger software efforts, currying and partial application reduce repetition in data processing and request handling where one or two parameters stay fixed across many calls.

4. Folds as a Unifying Pattern for Aggregation

The fold slides emphasize that many list aggregations differ only by: 1. an initial value 2. a combining function

The code snippet shows this directly:

def sum(xs: List[Int]) =
  xs.foldLeft(0)(_ + _)

def prod(xs: List[Int]) =
  xs.foldLeft(1)(_ * _)

def or(xs: List[Boolean]) =
  xs.foldLeft(false)(_ || _)

def and(xs: List[Boolean]) =
  xs.foldLeft(true)(_ && _)

And list append through a right fold:

def append(xs: List[Int])(ys: List[Int]) =
  xs.foldRight(ys)(_ :: _)

Once behavior is a function parameter, we can abstract over list traversal and keep only the aggregation intent.

Scala 3 Book pointers: - Collections methods (map, foldLeft, foldRight): https://docs.scala-lang.org/scala3/book/collections-methods.html - Lists and immutable data: https://docs.scala-lang.org/scala3/book/collections-classes.html

Practical relevance: Teams use folds to avoid ad hoc loops and duplicated boilerplate. The resulting code is easier to test, review, and optimize because each aggregator states exactly what it computes.

5. Recursion, Tail Recursion, and Runtime Behavior

The snippet includes two Fibonacci styles. First, memoized recursion with a mutable map:

def fibonacci(
  n: Int,
  memo: scala.collection.mutable.Map[Int, Int] = scala.collection.mutable.Map.empty
): Int =
  if n <= 0 then return 0
  if n == 1 then return 1
  if memo.contains(n) then return memo(n)
  val result = fibonacci(n - 1, memo) + fibonacci(n - 2, memo)
  memo(n) = result
  result
end fibonacci

Second, accumulator-based tail recursion:

def fib(n: Int, a: BigInt = 0, b: BigInt = 1): BigInt =
  require(n >= 0)
  if n == 0 then a
  else if n == 1 then b
  else fib(n - 1, b, a + b)
end fib

The tail recursion lecture highlights stack behavior: tail calls can be optimized to avoid growing activation records. Conceptually, this is why tail-recursive formulations are preferred for deep recursion.

Scala 3 Book pointers: - Recursive functions and control structures: https://docs.scala-lang.org/scala3/book/control-structures.html - @tailrec annotation: https://docs.scala-lang.org/scala3/book/annotations.html

Practical relevance: For production systems, tail-recursive code is often safer for large inputs and long-running workloads. Choosing among plain recursion, tail recursion, and memoization is a performance and maintainability design decision.

6. Type Context from Week 4: Static vs Dynamic

The static/dynamic types lecture frames why these examples feel robust in Scala: - in static typing, types are checked early (compile time) - compiler inference often keeps code concise - type signatures document valid operations

You can see this in function types such as (Int, Int) => Int and List[Int] => Int, which make behavior explicit and composable.

Scala 3 Book pointers: - Type inference and static typing basics: https://docs.scala-lang.org/scala3/book/taste-type-inference.html

Practical relevance: In large codebases, strong static types reduce integration errors and make refactoring safer, especially when higher-order functions are used heavily.

Summary

Week 4 connects three core ideas:

  1. Functions are values, so behavior can be passed and returned.
  2. Folds capture reusable aggregation patterns over collections.
  3. Tail-recursive formulations align functional clarity with runtime safety.

This progression is central to building scalable functional components: concise APIs, reusable behavior, and predictable performance.