Scala Default Parameters
Default parameters let you define a function where some parameters have pre-set values. When you call the function and omit those parameters, Scala automatically uses the defaults. This reduces the number of overloaded functions you need to write and makes function calls more concise.
Basic Default Parameter
def greet(name: String, greeting: String = "Hello"): String =
s"$greeting, $name!"
println(greet("Alice")) // Hello, Alice!
println(greet("Bob", "Good morning")) // Good morning, Bob!
println(greet("Carol", "Hi")) // Hi, Carol!
When you call greet("Alice"), Scala fills in greeting = "Hello" automatically. When you provide a second argument, it overrides the default.
Multiple Default Parameters
def createUser(
name: String,
role: String = "viewer",
active: Boolean = true,
maxSessions: Int = 1
): String =
s"User: $name | Role: $role | Active: $active | Sessions: $maxSessions"
println(createUser("Priya"))
// User: Priya | Role: viewer | Active: true | Sessions: 1
println(createUser("Ravi", "admin"))
// User: Ravi | Role: admin | Active: true | Sessions: 1
println(createUser("Deepa", "editor", false))
// User: Deepa | Role: editor | Active: false | Sessions: 1
println(createUser("Arun", "admin", true, 5))
// User: Arun | Role: admin | Active: true | Sessions: 5
Default Parameter Rules
Rule 1: Defaults go AFTER required parameters
─────────────────────────────────────────────
def f(required: Int, optional: String = "default") ✓ Correct
def f(optional: String = "default", required: Int) ✗ Confusing
Rule 2: You can skip middle defaults using named arguments
──────────────────────────────────────────────────────────
createUser("Sam", maxSessions = 3) // skips role and active defaults
Default Parameters vs Overloading
// Without default params — need many overloads
def sendMessage(to: String): Unit = sendMessage(to, "No subject", "")
def sendMessage(to: String, subject: String): Unit = sendMessage(to, subject, "")
def sendMessage(to: String, subject: String, body: String): Unit =
println(s"To: $to | Subject: $subject | Body: $body")
// With default params — one clean definition
def sendMessage(
to: String,
subject: String = "No subject",
body: String = ""
): Unit =
println(s"To: $to | Subject: $subject | Body: $body")
sendMessage("alice@example.com")
// To: alice@example.com | Subject: No subject | Body:
sendMessage("bob@example.com", "Hello")
// To: bob@example.com | Subject: Hello | Body:
Expressions as Defaults
Defaults can be any expression, not just literals. They are evaluated each time the function is called without that argument:
def logEvent(
message: String,
timestamp: Long = System.currentTimeMillis(),
level: String = "INFO"
): Unit =
println(s"[$level] [$timestamp] $message")
logEvent("Server started")
logEvent("User logged in", level = "DEBUG")
logEvent("Error occurred", level = "ERROR")
Each call to logEvent without a timestamp gets the current time at the moment of that call. The default expression re-evaluates every time.
Default Parameters in Case Classes
case class Config(
host: String = "localhost",
port: Int = 8080,
timeout: Int = 30,
maxRetries: Int = 3
)
val devConfig = Config() // all defaults
val prodConfig = Config("prod.server.com", 443, 60, 5)
val testConfig = Config(port = 9090) // only port changed
println(devConfig)
// Config(localhost,8080,30,3)
println(testConfig)
// Config(localhost,9090,30,3)
Default Parameters with Overriding
class EmailService:
def send(
to: String,
subject: String = "(no subject)",
cc: List[String] = List(),
bcc: List[String] = List(),
htmlBody: Boolean = false
): Unit =
println(s"Sending to $to | Subject: $subject | CC: $cc | HTML: $htmlBody")
val service = new EmailService()
service.send("a@b.com")
// Sending to a@b.com | Subject: (no subject) | CC: List() | HTML: false
service.send("a@b.com", "Welcome!", htmlBody = true)
// Sending to a@b.com | Subject: Welcome! | CC: List() | HTML: true
Practical: HTTP Request Builder
def httpRequest(
url: String,
method: String = "GET",
headers: Map[String, String] = Map("Content-Type" -> "application/json"),
timeout: Int = 5000,
retries: Int = 1
): String =
s"$method $url | timeout:${timeout}ms | retries:$retries"
println(httpRequest("https://api.example.com/users"))
// GET https://api.example.com/users | timeout:5000ms | retries:1
println(httpRequest("https://api.example.com/data", "POST", timeout = 10000))
// POST https://api.example.com/data | timeout:10000ms | retries:1
Limitations
// Cannot use a parameter as the default for another in the same list
def f(x: Int, y: Int = x + 1): Int = x + y // Error in some contexts
// Workaround: use multiple parameter lists or a different approach
def f(x: Int)(y: Int = x + 1): Int = x + y
println(f(5)()) // 11
println(f(5)(10)) // 15
Summary
With defaults Without defaults
────────────────────────────── ────────────────────────────────
One function definition Multiple overloaded definitions
Callers choose what to specify Callers must provide all values
Clear parameter names visible Intent less obvious
Defaults documented in signature Defaults hidden in overloads
Default parameters make APIs friendlier. Callers can start with simple calls and gradually use more options as needed, without being overwhelmed by required arguments for every use case.
