Scala best practice: Think “Expression-Oriented Programming”

This is an excerpt from the 1st Edition of the Scala Cookbook (partially modified for the internet). This is Recipe 20.3, “Scala best practice: Think "Expression-Oriented Programming".”

Problem

You’re used to writing statements in another programming language, and want to learn how to write expressions in Scala, and the benefits of the Expression-Oriented Programming (EOP) philosophy.

Solution

To understand EOP, you have to understand the difference between a statement and an expression. Wikipedia provides a concise distinction between the two:

Statements do not return results and are executed solely for their side effects, while expressions always return a result and often do not have side effects at all.”

So statements are like this:

order.calculateTaxes()
order.updatePrices()

Expressions are like this:

val tax = calculateTax(order)
val price = calculatePrice(order)

On Wikipedia’s EOP page, it also states:

“An expression-oriented programming language is a programming language where every (or nearly every) construction is an expression, and thus yields a value.”

As you might expect, it further states that all pure FP languages are expression-oriented.

The following example helps to demonstrate EOP. This recipe is similar to Recipe 20.1, so it reuses the class from that recipe to show a poor initial design:

// an intentionally bad example
class Stock (var symbol: String,
             var company: String,
             var price: String,
             var volume: String,
             var high: String,
             var low: String) {
    var html: String = _
    def buildUrl(stockSymbol: String): String = { ... }
    def getUrlContent(url: String):String = { ... }
    def setPriceUsingHtml() { this.price = ... }
    def setVolumeUsingHtml() { this.volume = ... }
    def setHighUsingHtml() { this.high = ... }
    def setLowUsingHtml() { this.low = ... }
}

Although I didn’t show it in that recipe, using this class would result in code like this:

val stock = new Stock("GOOG", "Google", "", "", "", "")
val url = buildUrl(stock.symbol)
stock.html = stock.getUrlContent(url)

// a series of calls on an object ('statements')
stock.setPriceUsingHtml
stock.setVolumeUsingHtml
stock.setHighUsingHtml
stock.setLowUsingHtml

Although the implementation code isn’t shown, all of these “set” methods extract data from the HTML that was downloaded from a Yahoo Finance page for a given stock, and then update the fields in the current object.

After the first two lines, this code is not expression-oriented at all; it’s a series of calls on an object to populate (mutate) the class fields, based on other internal data. These are statements, not expressions; they don’t yield values.

Recipe 20.1 showed that by refactoring this class into several different components, you would end up with the following code:

// a series of expressions
val url = StockUtils.buildUrl(symbol)
val html = NetUtils.getUrlContent(url)
val price = StockUtils.getPrice(html)
val volume = StockUtils.getVolume(html)
val high = StockUtils.getHigh(html)
val low = StockUtils.getLow(html)
val date = DateUtils.getDate
val stockInstance = StockInstance(symbol, date, price, volume, high, low)

This code is expression-oriented. It consists of a series of simple expressions that pass values into pure functions (except for getDate), and each function returns a value that’s assigned to a variable. The functions don’t mutate the data they’re given, and they don’t have side effects, so they’re easy to read, easy to reason about, and easy to test.

Discussion

In Scala, most expressions are obvious. For instance, the following two expressions both return results, which you expect:

scala> 2 + 2
res0: Int = 4

scala> List(1,2,3,4,5).filter(_ > 2)
res1: List[Int] = List(3, 4, 5)

However, it can be more of a surprise that an if/else expression returns a value:

val greater = if (a > b) a else b

Match expressions also return a result:

val evenOrOdd = i match {
    case 1 | 3 | 5 | 7 | 9 => println("odd")
    case 2 | 4 | 6 | 8 | 10 => println("even")
}

Even a try/catch block returns a value:

val result = try {
    "1".toInt
} catch {
    case _ => 0
}

Writing expressions like this is a feature of functional programming languages, and Scala makes using them feel natural and intuitive, and also results in concise, expressive code.

Benefits

Because expressions always return a result, and generally don’t have side effects, there are several benefits to EOP:

  • The code is easier to reason about. Inputs go in, a result is returned, and there are no side effects.
  • The code is easier to test.
  • Combined with Scala’s syntax, EOP also results in concise, expressive code.
  • Although it has only been hinted at in these examples, expressions can often be executed in any order. This subtle feature lets you execute expressions in parallel, which can be a big help when you’re trying to take advantage of modern multicore CPUs.

See Also

  • The Wikipedia definition of a statement, and the difference between a statement and an expression
  • Expression-Oriented Programming (EOP)