Application of Currying in Scala

As a Scala newbie I was struggling to understand the benefits of currying in a real-life scenario. Most of the examples I could find were a bit too academic for my taste. Eventually, the coin dropped and I realized that currying enables to derive specialized methods out of a general one in an elegant and concise way.

Let’s say you have just figured out the formula which calculates values of the Pascal’s triangle:

def pascal(c: Int, r: Int): Int =
  if (c == 0 || r == 0 || c == r) 1
  else pascal(c - 1, r - 1) + pascal(c, r - 1)

Now, the challenge is to visualize the resulting triangle. Without much effort, iterating over all rows and columns, one can quickly come up with an acceptable solution:

Pascal’s Triangle – the basic disproportional view


Those inclined to an excessive perfectionism will continue working hard until they see the output we could call balanced:

Pascal’s Triangle – the pretty-printed view


For the sake of the exercise, let’s assume that the visual aspect is considered crucial. We want to be flexible with how the output is being printed. Empowered by the knowledge of higher-order functions we quickly sketch out a prototypical template which:

  • centers around the key algorithm
  • accepts additional formatting instructions
  • delegates the output printing to the provided instructions which happen to be functions on their own
Here is how the initial template looks like:
def printTriangle
  (spaceFmt: Int => String,
   valueFmt: Int => String,
   rows: Int) = {
  def iterate(row: Int) {
    if (row <= rows) {
      if (row < rows) print(spaceFmt(rows - row))
      for (col <- 0 to row) print(valueFmt(pascal(col, row)))
      println()
      iterate(row + 1)
    }
  }
  iterate(0)
}

Next, we define the formatters as follows:

// Aligns the space around values
def prettySpace(x: Int) = ("%" + (x * 4) + "s").format("") 

// Ensures the value has enough space
def prettyValue(x: Int) = ("%8d").format(x)

Finally, we try it out and the output looks exactly as expected:

printTriangle(prettySpace, prettyValue, 10)
printTriangle(prettySpace, prettyValue, 15)
printTriangle(prettySpace, prettyValue, 20)

Our solution works but is far from being elegant. At the very least, having to pass all of the parameters is tedious and error-prone. This is where we want to be instead:

prettyPrint(10)
prettyPrint(15)
prettyPrint(20)

To achieve the goal above we could create the specialized implementation as follows:

def prettyPrint(rows: Int) =
  printTriangle(prettySpace, prettyValue, rows)

That way however, we are not taking the full advantage of what the functional programming has to offer. The task lends itself very well to currying. With a little change to the template method signature we turn it into a much more flexible piece of code, a big deal:

def printTriangle
  (spaceFmt: Int => String)
  (valueFmt: Int => String)
  (rows: Int) = {
  // The implementation remains unchanged
}

Note that there are three separate single-valued parameter lists. First two lists accept functions dealing with formatting, whereas the last one takes the user input. Now, we are able to define the specialized function with a minimum effort:

def prettyPrint = printTriangle(prettySpace)(prettyValue)_

The underscore at the end is important, if omitted the code won’t compile. As you can see we define only the parts which make the custom implementation special and skip the rest (underscore). In this particular case it won’t save us much of typing but you get the idea.

Before we wrap it up, let’s give the template the final touch by providing the default formatting:

def printTriangle
  (spaceFmt: Int => String = (x:Int) => "")
  (valueFmt: Int => String = (x:Int) => x + " ")
  (rows: Int) = {
  def iterate(row: Int) {
  // The implementation remains unchanged
}

With this improvement in place, declaring the default printer becomes fairly simple:

// The default (unformatted) print
def defaultPrint = printTriangle()()_

Finally, we test the two functions and all works as it should:

prettyPrint(10)
defaultPrint(15)

That concludes what I wanted say about currying in Scala. I am sure there are better examples the feature could be demonstrated on. Thanks for reading and looking forward to hearing from you.

View the Source Code

Similar Posts