Scala Methods and Functions
Methods and functions are core concepts in Scala programming. This chapter will detail method definitions, function literals, higher-order functions, and basic functional programming concepts.
Method Definitions
Basic Method Syntax
scala
// Basic method definition
def methodName(parameter1: Type1, parameter2: Type2): ReturnType = {
// Method body
returnValue
}
// Examples
def add(x: Int, y: Int): Int = {
x + y
}
def greet(name: String): String = {
s"Hello, $name!"
}
// Single-expression methods can omit braces
def multiply(x: Int, y: Int): Int = x * y
def square(x: Int): Int = x * xNo-Parameter Methods
scala
// No-parameter methods
def getCurrentTime(): Long = System.currentTimeMillis()
def pi(): Double = 3.14159
// Can omit parentheses (but call must also omit)
def randomNumber: Int = scala.util.Random.nextInt(100)
// Call methods
val time1 = getCurrentTime() // With parentheses
val time2 = getCurrentTime // Without parentheses (if defined with parentheses)
val number = randomNumber // Must be without parenthesesReturn Type Inference
scala
// Return type can be inferred
def add(x: Int, y: Int) = x + y // Inferred as Int
def createList() = List(1, 2, 3) // Inferred as List[Int]
// Recursive methods must explicitly declare return type
def factorial(n: Int): Int = {
if (n <= 1) 1
else n * factorial(n - 1)
}
// Complex methods should explicitly declare return type
def processData(data: List[String]): Map[String, Int] = {
data.groupBy(identity).view.mapValues(_.length).toMap
}Method Parameters
Default Parameters
scala
def greet(name: String, greeting: String = "Hello", punctuation: String = "!"): String = {
s"$greeting, $name$punctuation"
}
// Call methods
val msg1 = greet("Alice") // "Hello, Alice!"
val msg2 = greet("Bob", "Hi") // "Hi, Bob!"
val msg3 = greet("Charlie", "Hey", ".") // "Hey, Charlie."Named Parameters
scala
def createUser(name: String, age: Int, email: String, active: Boolean = true): String = {
s"User($name, $age, $email, $active)"
}
// Use named parameters
val user1 = createUser(
name = "Alice",
age = 25,
email = "alice@example.com"
)
val user2 = createUser(
email = "bob@example.com",
name = "Bob",
age = 30,
active = false
)Variable Arguments
scala
def sum(numbers: Int*): Int = {
numbers.sum
}
def concatenate(separator: String, strings: String*): String = {
strings.mkString(separator)
}
// Call methods
val total1 = sum(1, 2, 3, 4, 5)
val total2 = sum() // Empty argument list
val text1 = concatenate(", ", "apple", "banana", "cherry")
val text2 = concatenate(" - ") // Only separator
// Pass collection as variable arguments
val numbers = List(1, 2, 3, 4, 5)
val total3 = sum(numbers: _*) // Spread listCall-by-Name Parameters
scala
// Call-by-value parameter (default)
def callByValue(x: Int): Int = {
println("Evaluating call-by-value")
x + x
}
// Call-by-name parameter
def callByName(x: => Int): Int = {
println("Evaluating call-by-name")
x + x
}
// Test difference
def expensiveComputation(): Int = {
println("Computing...")
Thread.sleep(1000)
42
}
// Call-by-value: computed once, used twice
callByValue(expensiveComputation())
// Call-by-name: recomputed each time used
callByName(expensiveComputation())Function Literals
Anonymous Functions
scala
// Basic anonymous function syntax
val add = (x: Int, y: Int) => x + y
val square = (x: Int) => x * x
val greet = (name: String) => s"Hello, $name!"
// Use anonymous functions
val result1 = add(3, 4) // 7
val result2 = square(5) // 25
val message = greet("Alice") // "Hello, Alice!"Function Types
scala
// Function type declarations
val multiply: (Int, Int) => Int = (x, y) => x * y
val isEven: Int => Boolean = x => x % 2 == 0
val printer: String => Unit = s => println(s)
// Complex function types
val processor: List[Int] => List[Int] = list => list.map(_ * 2)
val validator: String => Option[String] = s =>
if (s.nonEmpty) Some(s) else NoneSimplified Syntax
scala
val numbers = List(1, 2, 3, 4, 5)
// Full syntax
val doubled1 = numbers.map(x => x * 2)
// Simplified syntax (placeholders)
val doubled2 = numbers.map(_ * 2)
val filtered = numbers.filter(_ > 2)
val sum = numbers.reduce(_ + _)
// Multiple placeholders
val pairs = numbers.zip(numbers).map { case (x, y) => x + y }
val pairsSimple = numbers.zip(numbers).map(_ + _) // Error: ambiguous
// Correct multi-parameter placeholders
val combined = List((1, 2), (3, 4)).map { case (a, b) => a + b }Higher-Order Functions
Accepting Functions as Parameters
scala
def applyOperation(x: Int, y: Int, operation: (Int, Int) => Int): Int = {
operation(x, y)
}
def applyToList[T, R](list: List[T], f: T => R): List[R] = {
list.map(f)
}
// Use examples
val add = (x: Int, y: Int) => x + y
val multiply = (x: Int, y: Int) => x * y
val sum = applyOperation(3, 4, add) // 7
val product = applyOperation(3, 4, multiply) // 12
val numbers = List(1, 2, 3, 4, 5)
val squared = applyToList(numbers, (x: Int) => x * x)
val strings = applyToList(numbers, (x: Int) => s"Number: $x")Methods that Return Functions
scala
def createMultiplier(factor: Int): Int => Int = {
(x: Int) => x * factor
}
def createValidator(minLength: Int): String => Boolean = {
(s: String) => s.length >= minLength
}
// Use examples
val double = createMultiplier(2)
val triple = createMultiplier(3)
val result1 = double(5) // 10
val result2 = triple(4) // 12
val isValidPassword = createValidator(8)
val isValidName = createValidator(2)
val valid1 = isValidPassword("secret123") // true
val valid2 = isValidName("Al") // trueFunction Composition
scala
// Function composition
def compose[A, B, C](f: B => C, g: A => B): A => C = {
(x: A) => f(g(x))
}
def andThen[A, B, C](f: A => B, g: B => C): A => C = {
(x: A) => g(f(x))
}
// Example functions
val addOne = (x: Int) => x + 1
val double = (x: Int) => x * 2
val toString = (x: Int) => x.toString
// Compose functions
val addOneThenDouble = compose(double, addOne) // Add 1 first, then multiply by 2
val doubleAndAddOne = andThen(double, addOne) // Multiply by 2 first, then add 1
val result1 = addOneThenDouble(3) // (3 + 1) * 2 = 8
val result2 = doubleAndAddOne(3) // (3 * 2) + 1 = 7
// Chain composition
val pipeline = andThen(andThen(addOne, double), toString)
val result3 = pipeline(3) // "8"Currying
Curried Function Definitions
scala
// Regular multi-parameter function
def add(x: Int, y: Int): Int = x + y
// Curried function
def addCurried(x: Int)(y: Int): Int = x + y
// Manual currying
def addManual(x: Int): Int => Int = (y: Int) => x + y
// Use examples
val result1 = add(3, 4) // 7
val result2 = addCurried(3)(4) // 7
val result3 = addManual(3)(4) // 7
// Partial application
val add5 = addCurried(5) _ // Int => Int
val result4 = add5(3) // 8
val add10 = addManual(10) // Int => Int
val result5 = add10(7) // 17Practical Currying Applications
scala
// Configuration functions
def createLogger(level: String)(component: String)(message: String): Unit = {
println(s"[$level] $component: $message")
}
// Create specific level loggers
val infoLogger = createLogger("INFO") _
val errorLogger = createLogger("ERROR") _
// Create specific component loggers
val dbInfoLogger = infoLogger("Database")
val apiErrorLogger = errorLogger("API")
// Use
dbInfoLogger("Connection established")
apiErrorLogger("Request failed")
// Data processing pipeline
def processData(validator: String => Boolean)
(transformer: String => String)
(data: List[String]): List[String] = {
data.filter(validator).map(transformer)
}
val isNotEmpty = (s: String) => s.nonEmpty
val toUpperCase = (s: String) => s.toUpperCase
val processor = processData(isNotEmpty)(toUpperCase) _
val result = processor(List("hello", "", "world", "scala"))
// List("HELLO", "WORLD", "SCALA")Partial Application Functions
Basic Partial Application
scala
def multiply(x: Int, y: Int, z: Int): Int = x * y * z
// Partial application
val multiplyBy2 = multiply(2, _, _) // (Int, Int) => Int
val multiplyBy2And3 = multiply(2, 3, _) // Int => Int
val result1 = multiplyBy2(3, 4) // 24
val result2 = multiplyBy2And3(5) // 30
// Use underscore for partial application
val numbers = List(1, 2, 3, 4, 5)
val doubled = numbers.map(multiply(2, _, 1)) // List(2, 4, 6, 8, 10)Complex Partial Application
scala
def createConnection(host: String, port: Int, timeout: Int, ssl: Boolean): String = {
s"Connection to $host:$port (timeout: ${timeout}s, ssl: $ssl)"
}
// Create specific environment connection functions
val prodConnection = createConnection("prod.example.com", 443, _, true)
val devConnection = createConnection("localhost", 8080, _, false)
val prod = prodConnection(30) // Production, 30s timeout
val dev = devConnection(5) // Development, 5s timeoutRecursive Functions
Tail Recursion Optimization
scala
import scala.annotation.tailrec
// Non-tail recursive (may cause stack overflow)
def factorial(n: Int): Int = {
if (n <= 1) 1
else n * factorial(n - 1)
}
// Tail recursive version
def factorialTailRec(n: Int): Int = {
@tailrec
def loop(n: Int, acc: Int): Int = {
if (n <= 1) acc
else loop(n - 1, n * acc)
}
loop(n, 1)
}
// Fibonacci sequence
def fibonacci(n: Int): Int = {
@tailrec
def fib(n: Int, a: Int, b: Int): Int = {
if (n == 0) a
else fib(n - 1, b, a + b)
}
fib(n, 0, 1)
}Mutual Recursion
scala
def isEven(n: Int): Boolean = {
if (n == 0) true
else isOdd(n - 1)
}
def isOdd(n: Int): Boolean = {
if (n == 0) false
else isEven(n - 1)
}
// Use examples
val even = isEven(4) // true
val odd = isOdd(5) // trueFunctional Programming Concepts
Pure Functions
scala
// Pure function: same input always produces same output, no side effects
def add(x: Int, y: Int): Int = x + y
def multiply(x: Int, y: Int): Int = x * y
// Impure function: has side effects
var counter = 0
def impureIncrement(): Int = {
counter += 1 // Side effect: modify external state
counter
}
def impureRandom(): Int = {
scala.util.Random.nextInt(100) // Side effect: non-determinism
}
// Pure function versions
def pureIncrement(current: Int): Int = current + 1
def pureRandom(seed: Long): (Long, Int) = {
val rng = new scala.util.Random(seed)
(seed + 1, rng.nextInt(100))
}Immutability
scala
// Immutable data structure operations
val numbers = List(1, 2, 3, 4, 5)
// All operations return new collections
val doubled = numbers.map(_ * 2)
val filtered = numbers.filter(_ > 2)
val sorted = numbers.sorted
val reversed = numbers.reverse
// Original list remains unchanged
println(numbers) // List(1, 2, 3, 4, 5)
// Functional update
case class Person(name: String, age: Int)
def updateAge(person: Person, newAge: Int): Person = {
person.copy(age = newAge)
}
val alice = Person("Alice", 25)
val olderAlice = updateAge(alice, 26)
// alice remains unchanged, olderAlice is a new instancePractical Application Examples
Data Processing Pipeline
scala
case class Employee(name: String, department: String, salary: Double, years: Int)
val employees = List(
Employee("Alice", "Engineering", 75000, 3),
Employee("Bob", "Sales", 65000, 5),
Employee("Charlie", "Engineering", 85000, 7),
Employee("Diana", "Marketing", 60000, 2),
Employee("Eve", "Engineering", 95000, 10)
)
// Functional data processing
def processEmployees(employees: List[Employee]): Map[String, Double] = {
employees
.filter(_.years >= 3) // Filter experienced employees
.groupBy(_.department) // Group by department
.view.mapValues(_.map(_.salary).sum) // Calculate total salary per department
.toMap
}
val departmentSalaries = processEmployees(employees)
// Use higher-order functions to create flexible processors
def createEmployeeProcessor(
filter: Employee => Boolean,
groupBy: Employee => String,
aggregate: List[Employee] => Double
): List[Employee] => Map[String, Double] = {
employees =>
employees
.filter(filter)
.groupBy(groupBy)
.view.mapValues(aggregate)
.toMap
}
// Create specific processors
val seniorEmployeeAvgSalary = createEmployeeProcessor(
_.years >= 5, // Filter condition
_.department, // Group condition
emps => emps.map(_.salary).sum / emps.length // Aggregate function
)Function Composition Examples
scala
// String processing functions
val trim = (s: String) => s.trim
val toLowerCase = (s: String) => s.toLowerCase
val removeSpaces = (s: String) => s.replaceAll("\\s+", "")
val capitalize = (s: String) => s.capitalize
// Compose functions
def pipe[A, B, C, D](f1: A => B, f2: B => C, f3: C => D): A => D = {
a => f3(f2(f1(a)))
}
val normalizeString = pipe(trim, toLowerCase, removeSpaces)
val formatTitle = pipe(trim, toLowerCase, capitalize)
// Use examples
val input = " Hello World "
val normalized = normalizeString(input) // "helloworld"
val title = formatTitle(input) // "Hello world"
// More complex pipeline
val processText: String => List[String] = { text =>
text
.split("\\.")
.map(trim)
.filter(_.nonEmpty)
.map(capitalize)
.toList
}
val sentences = processText("hello world. this is scala. functional programming is great.")
// List("Hello world", "This is scala", "Functional programming is great")Exercises
Exercise 1: Higher-Order Function Implementation
scala
object HigherOrderFunctionExercise {
// Implement map function
def myMap[A, B](list: List[A], f: A => B): List[B] = {
list match {
case Nil => Nil
case head :: tail => f(head) :: myMap(tail, f)
}
}
// Implement filter function
def myFilter[A](list: List[A], predicate: A => Boolean): List[A] = {
list match {
case Nil => Nil
case head :: tail =>
if (predicate(head)) head :: myFilter(tail, predicate)
else myFilter(tail, predicate)
}
}
// Implement reduce function
def myReduce[A](list: List[A], f: (A, A) => A): A = {
list match {
case Nil => throw new IllegalArgumentException("Empty list")
case head :: Nil => head
case head :: tail => f(head, myReduce(tail, f))
}
}
def main(args: Array[String]): Unit = {
val numbers = List(1, 2, 3, 4, 5)
val doubled = myMap(numbers, (x: Int) => x * 2)
val evens = myFilter(numbers, (x: Int) => x % 2 == 0)
val sum = myReduce(numbers, (x: Int, y: Int) => x + y)
println(s"Original: $numbers")
println(s"Doubled: $doubled")
println(s"Evens: $evens")
println(s"Sum: $sum")
}
}Exercise 2: Functional Calculator
scala
object FunctionalCalculator {
type Operation = (Double, Double) => Double
val add: Operation = _ + _
val subtract: Operation = _ - _
val multiply: Operation = _ * _
val divide: Operation = (x, y) => if (y != 0) x / y else throw new ArithmeticException("Division by zero")
def calculate(x: Double, y: Double, op: Operation): Double = op(x, y)
def createCalculator(op: Operation): (Double, Double) => Double = op
// Chain calculations
def chain(initial: Double, operations: List[(Operation, Double)]): Double = {
operations.foldLeft(initial) { case (acc, (op, value)) =>
op(acc, value)
}
}
def main(args: Array[String]): Unit = {
// Basic calculations
println(calculate(10, 5, add)) // 15.0
println(calculate(10, 5, multiply)) // 50.0
// Create specialized calculators
val adder = createCalculator(add)
val multiplier = createCalculator(multiply)
println(adder(3, 4)) // 7.0
println(multiplier(3, 4)) // 12.0
// Chain calculations: (10 + 5) * 2 - 3 / 3
val result = chain(10, List(
(add, 5),
(multiply, 2),
(subtract, 3),
(divide, 3)
))
println(s"Chain result: $result") // 27.0
}
}Summary
This chapter detailed core concepts of methods and functions in Scala:
- Method definitions: Basic syntax, parameter handling, return types
- Function literals: Anonymous functions, function types, simplified syntax
- Higher-order functions: Methods that accept and return functions
- Currying: Step-by-step application of function parameters
- Recursion: Tail recursion optimization and mutual recursion
- Functional programming: Pure functions, immutability, function composition
Mastering these concepts is the foundation for functional programming and the key to understanding Scala's powerful expressiveness. In the next chapter, we will learn Scala Closures, diving deeper into function scope and variable capture mechanisms.