Scala is a hybrid language implementing both functional and object-oriented features. One of them that merits to be analyzed is currying.
Data Engineering Design Patterns

Looking for a book that defines and solves most common data engineering problems? I wrote
one on that topic! You can read it online
on the O'Reilly platform,
or get a print copy on Amazon.
I also help solve your data engineering problems 👉 contact@waitingforcode.com 📩
This post composed of 4 sections focused on functions currying. The first part shortly explains what it is. The next one presents how the currying can be used in Scala. The third part focuses more on the aspect of default arguments. The final section contains some tests showing the practical uses of currying.
Currying definition
Accordingly to the Wikipedia's definition, the currying is:
" the technique of translating the evaluation of a function that takes multiple arguments (or a tuple of arguments) into evaluating a sequence of functions, each with a single argument.
If we would like to illustrate that, we could tell that currying consists on transforming any function composed of multiple parameters to a sequence of functions where each function has a single one parameter: f(param1, param2) is from that decomposed to following functions: f1 = f(param1) and result = f1(param2).
Currying in Scala code
More concretely in Scala, the currying can be written as:
def multiply(multiplier1: Int)(multiplier2: Int) val multiplyBy2 = multiply(2) val result2multipliedBy4 = multiplyBy2(4)
The version above is called shorthand because Scala has also more verbose style where the multiply method could be written like this:
def multiply(multiplier1: Int): (Int => Int) = { (multiplier2: Int) => { multiplier1 * multiplier2 } } val multiplyBy2 = multiply(2) val result5multipliedBy4 = multiplyBy2(4)
As you can see, we declare a method returning a method taking an Int and returning another Int. It looks simple for the function with 2 parameters but you can simply discover what happens if there would be more: (Int => Int => Int => Int => Int ....). This mode of writing can sometimes appear to be too verbose.
Another way to curry a function consists on the use of FunctionX's (where X is the arity [number of arguments]) curried method:
val concatenate = (separator: String, word1: String, word2: String) => s"$word1$separator$word2" val concatenateCurried: (String) => (String) => (String) => (String) = concatenate.curried
At this occasion you'd also notice that any function can be uncurried with scala.Function.uncurried method. It applies only to the functions of the arity between 2 and 5.
The currying applies to any Scala's function and it's similar to other concept coming from functional programming, the partially applied functions. But to not introduce too many new topics at once, they'll be explained in one of further posts.
Currying and default arguments
It's important to highlight the fact that the currying doesn't work well with the default arguments of the method. Thus, the following code won't compile:
def concatenateWords(word1: String)(separator: String = ","): String = { s"$word1$separator" } val concatenateWithThePrefix = concatenateWords("test") val a = concatenateWithThePrefix()
The compilation error was:
Error:(27, 50) missing argument list for method concatenateWords in class ScalableBloomFilterTest Unapplied methods are only converted to functions when a function type is expected. You can make this conversion explicit by writing `concatenateWords _` or `concatenateWords(_)(_)` instead of `concatenateWords`. val concatenateWithThePrefix = concatenateWords("the")
The solution for that could be the use of apply method defined inside a class, as shown:
class WordsConcatenator(word1: String) { def apply(separator: String = " "): String = { s"$word1$separator" } } def wordsConcatenatorWithDefault = new WordsConcatenator("the") def concatenateWithThePrefix = wordsConcatenatorWithDefault
Currying examples
The tests in this section contain some examples of currying:
describe("Scala") { it("should uncurry the function with the 3 parameters") { def concatenate(separator: String)(word1: String)(word2: String): String = { s"$word1$separator$word2" } val uncurriedConcatenate = Function.uncurried(concatenate _) val aAndBCommaSeparated = uncurriedConcatenate(",", "A", "B") aAndBCommaSeparated shouldEqual "A,B" } it("should apply currying to the function with 3 parameters through .curried helper function") { val concatenate = (separator: String, word1: String, word2: String) => s"$word1$separator$word2" val concatenateCurried: (String) => (String) => (String) => (String) = concatenate.curried val concatenateWithComma: (String) => (String) => (String) = concatenateCurried(",") val concatenateWithCommaAndA: (String) => (String) = concatenateWithComma("A") val aAndBCommaSeparated = concatenateWithCommaAndA("B") aAndBCommaSeparated shouldEqual "A,B" } it("should apply currying to the function without default arguments") { def concatenate(separator: String)(word1: String)(word2: String): String = { s"$word1$separator$word2" } def concatenateWithComma = concatenate(",") _ val aAndBCommaSeparated = concatenateWithComma("A")("B") aAndBCommaSeparated shouldEqual "A,B" } it("should define currying in more verbose manner") { def concatenate(separator: String): (String => String => String) = { (word1: String) => { (word2: String) => { s"$word1$separator$word2" } } } val concatenateWithComma = concatenate(",") val aAndBCommaSeparated = concatenateWithComma("A")("B") aAndBCommaSeparated shouldEqual "A,B" } it("should not keep the default arguments of the method") { class WordsConcatenator(word1: String) { def apply(separator: String = " "): String = { s"$word1$separator" } } def wordsConcatenatorWithDefault = new WordsConcatenator("the") def concatenateWithThePrefix = wordsConcatenatorWithDefault val end = concatenateWithThePrefix() end shouldEqual "the" } }
Currying is a useful functional programming feature implemented in Scala. As presented in the first section, its goal consists on simplifying the functions by reducing the number of parameters. Two writing methods are available categorized in the post as less and more verbose. As shown in the third part, the currying applies with difficulty to the methods having default arguments. In order to use it in this kind of functions, a small hack with the use of class and its apply method is required. The last section presented some use cases of currying, focusing especially on described grammar.
Consulting

With nearly 16 years of experience, including 8 as data engineer, I offer expert consulting to design and optimize scalable data solutions.
As an O’Reilly author, Data+AI Summit speaker, and blogger, I bring cutting-edge insights to modernize infrastructure, build robust pipelines, and
drive data-driven decision-making. Let's transform your data challenges into opportunities—reach out to elevate your data engineering game today!
👉 contact@waitingforcode.com
đź”— past projects