Multiple Parameter Lists

Learn to define functions with multiple parameter lists in Scala for Python developers. Master currying and partial application in this hands-on tutorial.

Focus: define functions with multiple parameter lists

Sponsored

If you've come from Python, you're used to defining functions with a single parameter list: def greet(name, greeting): .... But Scala gives you a superpower that Python lacks — multiple parameter lists — and once you master it, you'll unlock a whole new level of functional expressiveness. In this lesson, you'll learn how to define functions with multiple parameter lists, why Scala does this, and how it enables currying, partial application, and clean, readable APIs. This is a step that will change how you think about function design — let's dive in.

The problem this lesson solves

When you're building real-world applications, you often want to reuse a function with the same fixed configuration across many calls. In Python, you might do this:

# Python: manually fixing arguments
def connect(host, port, timeout):
    return f"Connecting to {host}:{port} with timeout {timeout}"

# Reuse with the same host and port
connect("localhost", 8080, 30)
connect("localhost", 8080, 60)

That works, but it repeats arguments and forces you to keep them in the right order every time. The pain becomes worse when you have many parameters — you'll find yourself passing the same configuration over and over. In Scala, multiple parameter lists let you bake in the common parameters and then call the remaining ones freely. This isn't just syntax sugar — it enables clean partial application and smoother composition, two concepts you'll use in functional programming all the time.

Core concept / mental model

Think of a function with multiple parameter lists as a vending machine with separate slots that accept coins one at a time. You insert the first coin, then the second, and so on — you can stop at any point and carry the machine with you. Each parameter list is a separate currying step: you give the function one group of arguments, and it returns a new function that expects the next group.

In Scala, a function with two parameter lists looks like this:

def greet(greeting: String)(name: String): String = s"$greeting, $name!"

When you call greet("Hello")("Alice"), you provide both groups. But you can also say val helloGreeter = greet("Hello") — that gives you a function expecting just the name. This is partial application in action.

Key idea: Every parameter list creates a new function. The last parameter list is what actually returns the final result, but intermediate lists return functions that "remember" the earlier arguments.

How it works step by step

Step 1: Define the function with multiple parameter lists

In Scala, you simply write multiple parentheses ( ) groups in your def declaration. Each group can have one or more parameters. Example:

def add(a: Int)(b: Int): Int = a + b

This defines a function add that takes two parameter lists: the first with a, the second with b. You call it as add(2)(3) — the result is 5.

Step 2: Understand what the compiler sees

For a method with two parameter lists, the Scala compiler treats it as a curried function. Under the hood, def add(a: Int)(b: Int): Int is roughly equivalent to a function that takes a and returns a function that takes b. You can see this by using the underscore _ to create a partially applied function:

val add2 = add(2) _    // underscore lifts it to a function
println(add2(3))       // prints 5

Step 3: Turn it into a function value with _

The underscore _ is crucial when you want to convert a method into a function value — this is called lifting. Without the underscore, Scala might complain that you're missing the second parameter list. Why is that useful? Because it lets you pass the partially applied function to other higher-order functions like map or foreach.

Hands-on walkthrough

Let's make it concrete. Suppose you're building a simple logging system. You want to log messages at different levels but always include a timestamp. With multiple parameter lists, you can fix the formatting first, then the level, then the message.

def log(formatter: String => String)(level: String)(message: String): Unit = {
  val formatted = formatter(message)
  println(s"[$level] $formatted")
}

// A simple formatter that uppercases
val upperFormatter: String => String = _.toUpperCase

val infoLogger = log(upperFormatter)("INFO") _   // fix formatter and level
infoLogger("User logged in")   // prints [INFO] USER LOGGED IN
infoLogger("Database connected") // prints [INFO] DATABASE CONNECTED

Expected output:

[INFO] USER LOGGED IN
[INFO] DATABASE CONNECTED

Now let's compare with Python. In Python, you can achieve similar behavior with functools.partial, but it's not a first-class language feature:

from functools import partial

def log(formatter, level, message):
    print(f"[{level}] {formatter(message)}")

upper = lambda s: s.upper()
info_logger = partial(log, upper, "INFO")
info_logger("User logged in")
info_logger("Database connected")

The Python version works, but Scala's multiple parameter lists make the partial application part clearer and more idiomatic — you don't need an extra import or helper function.

A practical exercise: curried mathematical operations

Let's build a small calculator that uses multiple parameter lists to create custom operations.

def operate(op: String)(a: Double)(b: Double): Double = op match {
  case "+" => a + b
  case "-" => a - b
  case "*" => a * b
  case "/" => if (b != 0) a / b else throw new IllegalArgumentException("Division by zero")
  case _ => throw new IllegalArgumentException(s"Unknown operator: $op")
}

val add = operate("+") _   // (a: Double) => (b: Double) => a + b
val add10 = add(10) _       // (b: Double) => 10 + b
println(add10(5))           // 15.0
println(operate("*")(3)(4)) // 12.0

Here, add10 is a function that adds 10 to any number. This pattern is incredibly handy when you want to reuse a customized version of a function throughout your codebase.

Compare options / when to choose what

In Scala, you have several ways to define a function with multiple parameters. Here's a comparison table to help you choose:

Approach Example Use case Pros Cons
Multiple parameter lists def f(a: Int)(b: Int) = a + b Currying, partial application, implicit parameter lists Natural currying, allows implicit, enables partial application with _ More syntax, may confuse newbies
Single parameter list def f(a: Int, b: Int) = a + b Simple functions where no partial application needed Straightforward, familiar from Python No easy currying, must repeat all args
Returning a function def f(a: Int): Int => Int = b => a + b When you want to be explicit about returning a function Explicit, shows the function type More verbose, less idiomatic
Using curried method def f(a: Int)(b: Int) = a + b; val g = f.curried When you have an existing method and need a curried version Can convert later Requires extra step

When to choose what? Use multiple parameter lists when you want to:

  • Enable partial application for configuration reuse.
  • Define implicit parameter lists (covered in a later lesson).
  • Improve readability when you have logical groups of parameters.

Stick to a single parameter list when your function is simple and rarely used in a partially applied way.

Troubleshooting & edge cases

missing argument list error

If you call a function with multiple parameter lists but only provide the first list, Scala will raise a compile-time error: "missing argument list for method ..." This may surprise you if you expected partial application without the underscore. Example:

def greet(greeting: String)(name: String) = s"$greeting $name"
val g = greet("Hello")   // error: missing argument list

Fix: Add an underscore to lift it into a function: val g = greet("Hello") _.

Forgetting the underscore when assigning to a function variable

If you write val f = someMethod _ but the method has multiple parameter lists, you need the underscore after all parameter lists to get a function value. For example:

def add(a: Int)(b: Int) = a + b
val addCurried = add _       // works: (Int) => (Int) => Int
val addPartial = add(1) _    // works: (Int) => Int

If you miss the underscore, you'll get an error.

Overloading with multiple parameter lists can confuse

If you have overloaded methods with the same name but different parameter lists, Scala may have trouble resolving the right one. This can lead to ambiguity errors. Keep overloads simple, or avoid them when using curried style.

Edge case: Empty parameter lists

You can have an empty parameter list: def f()(a: Int) = a. Calling f()(5) works, but f(5) will not. This can be a source of subtle bugs, so avoid empty lists unless you have a good reason.

What you learned & what's next

You now understand how to define functions with multiple parameter lists in Scala: you saw the mental model of currying, learned to use partial application with the underscore, and compared it to Python's functools.partial. You also practiced a real-world logging example and avoided common pitfalls like the missing argument list error.

Key takeaways from this lesson:

  • Multiple parameter lists allow step-by-step argument application.
  • The underscore _ converts a partially applied method into a function value.
  • Currying enables clean configuration reuse and function composition.

What's next? In the next lesson, you'll explore partial application and currying in more depth, including how to use these techniques with higher-order functions like map and flatMap. Get ready to take your functional programming skills to the next level!

Now, continue to the next lesson in this track to deepen your understanding of currying and partial application.

Practice recap

Try defining a configure function with three parameter lists (e.g., connection config, request params, and callbacks). Use partial application to create a 'production-ready' client that only takes request params. Then try lifting it with _ and passing it to map to transform a list of requests.

Common mistakes

  • Forgetting the underscore when assigning a partially applied function — val f = greet("Hello") errors; add _ to lift it to a function.
  • Confusing a multiple parameter list function with a single parameter list — ensuring you call add(1)(2) and not add(1, 2).
  • Using empty parameter lists unintentionally, causing calls like f()(5) instead of f(5) — avoid empty lists unless needed.
  • Overloading curried methods can cause ambiguity — keep overloaded names simple or use distinct names.

Variations

  1. Using the curried method on an existing method to convert a single parameter list into a curried function.
  2. Using functools.partial in Python for comparison — but it's not a language feature.
  3. Defining a function that returns a lambda: def f(a: Int) = (b: Int) => a + b for explicit function types.

Real-world use cases

  • Building a database query API where you fix the connection config first, then the query string — use currying for clean config reuse.
  • Creating a logging utility with a fixed formatter and level, then passing the message each time — avoids repeating config.
  • Designing a mathematical library where you pre-define operators (add, multiply) and then apply them to numbers in data pipelines.

Key takeaways

  • Multiple parameter lists enable currying and partial application in Scala.
  • Use the underscore _ to lift a method into a function value.
  • Partial application lets you fix configuration arguments and reuse the function easily.
  • Compare with Python's functools.partial to understand the difference.
  • Be careful with missing argument list errors and overloaded curried methods.

Sponsored

Sponsored

Discussion

Questions, corrections, and tips help everyone reading this page.

0 comments

Add a comment

Shown publicly with your comment.

Be constructive · max 4,000 characters

No comments yet — start the thread.

Related tutorials, quizzes, and articles for this topic.