net2020

Haskell Curry and curried functions in Scala

8/25/2015

Haskell Curry and curried functions in Scala

Introduction

Haskell Curry (1900-1982) was a mathematician. He worked on combinatory logic, a topic in mathematics. Combinatory logic became a basis for functional programming languages.

From Wikipedia:

Functional programming treats computation as the evaluation of mathematical functions and avoids changing-state and mutable data.

Haskell is one example of a functional programming language. It was named 'haskell' after Haskell Curry's first name. Terms currying and curried come from his last name.

Curried functions in Scala

An example of curried function (Scala):

private def multiplyCurried(a: Int)(b: Int) = a * b

Notice separate argument lists:

(a: Int)
(b: Int)

Curried function can be used as a partially applied function (do not confuse with partial functions):

val multiplyBy2 = multiplyCurried(2) _

Notice data type of multiplyBy2:

$$anonfun$1

It's an anonymous function created by binding a value to the first argument of multiplyCurried. What you get back is a function that takes one less argument. Usage example:

println(multiplyBy2(3)) //prints 6

Another example:

private def processLines[A](fileName: String)(f: (Seq[String]) => A): A = {
	val input = Source.fromFile(fileName)
	try {
		val output = ListBuffer.empty[String]
		input.getLines.foreach(output+=) f(output)
	} finally {
		Option(input) foreach (_.close())
	}
}

Usage:

val useLines = processLines[Seq[String]]("properties.txt") _
val linesWithNumbers = useLines { lines => lines.zipWithIndex.map { t => "line: %d, text: %s".format(t._2, t._1) } }
linesWithNumbers.foreach(println)

val lineLengths = useLines(_.map(line => "line length: %d".format(line.length())))
lineLengths.foreach(println)

How to use curried functions

Once curried function is partially applied (some of its arguments are bound) it can be passed as a function with limited parameter list.

Curried functions are great for code reuse (function can be bound to input data that is computed once but processed in different ways) or parallel processing (partially applied function can be further processed in separate threads).

Curried functions can also help with refining a generic functionality. For example:

private def customFilter(findOdd: Boolean)(number: Int): Boolean = if (findOdd) number % 2 != 0 else number % 2 == 0

val numbers = Seq(1, 2, 3, 4, 5)
val evenFilter = customFilter(false)_
val oddFilter = customFilter(true)_

numbers.filter(evenFilter).foreach(println)
numbers.filter(oddFilter).foreach(println)

In Scala sequence filter function expects a parameter of this type:

f:(x) => Boolean

The two partially applied functions we defined (evenFilter and oddFilter) match that type after being derived from our curried function customFilter.

Partially applied vs partial functions

For explanation of partial functions (functions that are defined for a limited set of input arguments) see this post: Scala partial functions (without a PhD)