[Scala] Reducing code duplication using higher-order function

mj park
3 min readAug 23, 2021

Overview

  • What is “Higher-Order Function”?
  • How can we use higher order function to reduce code duplication?

Higher-Order Function

In a common programming language, you can create a function to reduce a repeated operation. For instance, if you were repeating an addition of three numbers like below

val added1 = 1 + 2 + 3
val added2 = 2 + 3 + 4
...

You could simply create a function that adds three parameters

def add(first: Int, second: Int, third: Int): Int = {
first + second + third
}
val added1 = add(1,2,3)
val added2 + add(2,3,4)

Well, though it doesn’t reduce a whole lot of code duplication, you get the idea :)

In a similar context, higher-order function is a function that takes a function as its parameter. In Scala, passing a function to another function can help you reduce code duplication. For instance, exampleFunction takes a function called anotherFunction which takes a String as a parameter and returns a Boolean value.

def exampleFunction(anotherFunction: String => Boolean) = {
val name = "mj park"
anotherFunc(name)
}

I am going to show you real world examples below.

Problem

Suppose you implemented a program that allows a user to explore the files in the current directory. Later, a user requested a new feature that filters files with the suffix query. So, you ended up writing a function like below

// original code
object FileMatcher {
private def filesHere = (new File(".")).listFiles

def filesEnding(suffix: String) = {
for {
file <- filesHere
if file.getName.endsWith(suffix)
} yield file
}
}

In the next version, you want to support a feature that filters files with the prefix query. The first thing that could come up in your mind is using a similar function filesStarting

// original code
object FileMatcher {
private def filesHere = (new File(".")).listFiles
...
def fileEnding(suffix: String) = {
...
}
def filesStarting(prefix: String) = {
for {
file <- filesHere
if (file.getName.startsWith(prefix))
} yield file
}
}

Well, this is whole lot of repetition, and we should avoid it.

Idea

Thinking about common components between two functions — filesEnding and filesStarting , we could easily notice that it returns a list of files using a different operation on each fileName. What would look like if we were to get a “function” as a parameter that does the same operation?

def filesMatching(query: String, FUNCTION) = {
for {
file <- filesHere
if (file.getName.FUNCTION(uqery))
} yield file
}

As shown here, we could just apply passed “FUNCTION” to each file to filter. Using this idea, we pass the function as an argument and turn it into something like below

object FileMatcher {
private def filesHere = (new File(".")).listFiles

def filesEnding(query: String) = {
getMatchingFiles(query, (fileName, query) => fileName.endsWith(query))
}
def fileStarting(query: String) = {
getMatchingFiles(query, (fileName, query) => fileName.startsWith(query))
}

private def getMatchingFiles(query: String, func: (String, String) => Boolean) = {
for {
file <- filesHere
if (func(file.getName, query))
} yield file
}
}

getMatchingFiles applies passed function in its function body. In fact, we could make even simpler using a placeholder (_)

object FileMatcher {
private def filesHere = (new File(".")).listFiles

def filesEnding(query: String) = {
getMatchingFiles(_.endsWith(query))
}

def fileStarting(query: String) = {
getMatchingFiles(_.startsWith(query))
}

private def getMatchingFiles(func: String => Boolean) = {
for {
file <- filesHere
if
func(file.getName)
} yield file
}
}

Here is what’s happening in filesEnding and filesStarting

  • It passes a function (WHAT TO DO) with query
  • A helper function, getMatchingFiles, applies passed function using the filename, returning a list of files that passed the condition

Conclusion

I think it does take some practices until you get familiar with higher-order function. But the key is to extract the common components out of the functions. I hope this short article gives a better understanding on how to apply higher-order function to reduce code duplication.

Reference

Chapter 9.1 Reducing code duplication — Programming in Scala

--

--

mj park

Software Engineer | Scala | Functional Programming