Functions
## Learning Objectives
- Master function definitions with def
- Understand function parameters and return types
- Learn recursion
- Work with anonymous functions
- Understand closures and partial applications
## Function Basics
### Defining Functions
```scala
def greet(): String = {
"Hello, World!"
}
println(greet()) // Hello, World!
```
### Function with Parameters
```scala
def add(a: Int, b: Int): Int = {
a + b
}
println(add(3, 5)) // 8
```
### Return Type Inference
Scala can infer return types for expressions:
```scala
def add(a: Int, b: Int) = a + b // Return type inferred as Int
def greet() = "Hello" // Return type inferred as String
```
### Unit Return Type
```scala
def printSum(a: Int, b: Int): Unit = {
println(s"Sum: ${a + b}")
}
printSum(3, 5) // Sum: 8
```
## Parameter Types
### Default Parameters
```scala
def greet(name: String = "World") = s"Hello, $name!"
greet() // Hello, World!
greet("Alice") // Hello, Alice!
```
### Named Arguments
```scala
def connect(host: String = "localhost", port: Int = 8080) = {
s"$host:$port"
}
connect(port = 9000, host = "server") // server:9000
```
### Variable Arguments (Varargs)
```scala
def sum(numbers: Int*): Int = {
numbers.sum
}
sum(1, 2, 3, 4, 5) // 15
sum(1, 2, 3) // 6
```
### Parameter Groups
```scala
def addAndMultiply(a: Int)(b: Int)(c: Int): Int = {
(a + b) * c
}
addAndMultiply(1)(2)(3) // 9
```
## Recursion
### Basic Recursion
```scala
def factorial(n: Int): BigInt = {
if (n <= 1) 1
else n * factorial(n - 1)
}
factorial(5) // 120
```
### Tail Recursion
Tail-recursive functions are optimized to avoid stack overflow:
```scala
def factorialTail(n: Int): BigInt = {
@annotation.tailrec
def loop(acc: BigInt, n: Int): BigInt = {
if (n <= 1) acc
else loop(acc * n, n - 1)
}
loop(1, n)
}
factorialTail(10000) // Works without stack overflow
```
### 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)
isEven(10) // true
```
## Anonymous Functions
### Basic Syntax
```scala
val addOne = (x: Int) => x + 1
addOne(5) // 6
```
### Multiple Parameters
```scala
val add = (a: Int, b: Int) => a + b
add(3, 5) // 8
```
### No Parameters
```scala
val getTime = () => System.currentTimeMillis()
getTime()
```
### Placeholder Syntax
```scala
val numbers = List(1, 2, 3, 4, 5)
numbers.map((x: Int) => x * 2)
numbers.map(x => x * 2)
numbers.map(_ * 2) // Placeholder for single parameter
val add = (_: Int) + (_: Int) // Multiple placeholders
add(3, 5) // 8
```
## Higher-Order Functions
Functions that take functions as parameters or return functions:
```scala
def applyTwice(f: Int => Int, x: Int): Int = {
f(f(x))
}
def double(x: Int) = x * 2
applyTwice(double, 5) // 20 (5 * 2 * 2)
```
### Functions as Return Values
```scala
def multiplier(factor: Int): Int => Int = {
(x: Int) => x * factor
}
val triple = multiplier(3)
triple(5) // 15
```
## Closures
A closure is a function that captures variables from its enclosing scope:
```scala
var factor = 10
val multiply: Int => Int = (x: Int) => x * factor
multiply(5) // 50
factor = 20
multiply(5) // 100 (closure sees new value)
```
## Currying
### Curried Functions
```scala
def curriedSum(a: Int)(b: Int): Int = a + b
curriedSum(3)(5) // 8
val addFive = curriedSum(5) _
addFive(3) // 8
```
### Why Currying?
```scala
def withTimestamp(log: String => Unit)(message: String): String = {
val timestamp = System.currentTimeMillis()
log(s"[$timestamp] $message")
message
}
def logToConsole(msg: String) = println(msg)
def logToFile(msg: String) = /* write to file */ ()
val timestampedLog = withTimestamp(logToConsole) _
timestampedLog("Hello") // [1234567890] Hello
```
## Special Function Syntax
### Infix Notation
```scala
object Math {
def +(a: Int, b: Int): Int = a.+(b)
def -(a: Int, b: Int): Int = a.-(b)
}
```
### Operators as Methods
```scala
val list = List(1, 2, 3, 4, 5)
list.fold(0)(_ + _) // 15
list.foldLeft(0)(_ + _) // Same
(0 /: list)(_ + _) // Same (foldLeft with /: operator)
```
## Method vs Function
### Method Definition
```scala
object Calculator {
def add(a: Int, b: Int): Int = a + b
}
```
### Convert Method to Function
```scala
val addFunction: (Int, Int) => Int = Calculator.add _
addFunction(3, 5) // 8
```
## Summary
- Use `def` to define functions
- Parameters have explicit types; return types can be inferred
- Default parameters and named arguments increase flexibility
- Varargs (`_*`) accept variable number of arguments
- Recursion is fundamental in functional programming; use tail recursion for efficiency
- Anonymous functions provide concise function literals
- Higher-order functions take or return functions
- Closures capture environment variables
- Currying splits multi-parameter functions into single-parameter chains
- Methods are defined in classes/objects; functions are values
Comments
Comments powered by Giscus
To enable comments, add your Giscus embed code here.
Learn more about Giscus →