Expression-Oriented Programming (EOP)

Introduction

  • Statements vs expressions

    • statements are used for side-effects
    • expressions always yield a result
  • Wikipedia:

“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.”

  • All Scala control structures can be used as expressions (except while)

Statement examples

println("hi")
if a == b then println("equal")

Expression examples

if/then is an expression:

val min = if a < b then a else b

def min(a: Int, b: Int): Int =
    if a < b then a else b

match is an expression:

val evenOrOdd = i match
    case 1 | 3 | 5 | 7 | 9 => "odd"
    case 2 | 4 | 6 | 8 | 10 => "even"
    case _ => "other"

for is an expression:

val xs = List(1, 2, 3)
val ys = for x <- xs yield x * 2

try/catch is an expression:

def makeInt(s: String): Int =
    try
        s.toInt
    catch
        case e: Exception => 0

Expressions can be used as the body of functions

Because all of these can be used as expressions, they can also be used as the body of a function, and you’ll see this all the time:

def makeInt(s: String): Option[Int] =
    try
        Some(s.toInt)
    catch
        case _: NumberFormatException => None

def isTrue(a: Matchable): Boolean = a match
    case 0 | "" | "0" | false => false
    case _     => true

def min(a: Int, b: Int): Int =
    if a < b then a else b

EOP 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 write, read, and test. Inputs go into a pure function, a result is returned, it’s assigned to an immutable variable, and there are no side effects.
  • Pure functions are easier to test. You don’t have to set up some mutable global state to test them.
  • Combined with Scala’s syntax, EOP results in concise, expressive code.
  • Although it’s 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 is a huge bonus when you’re trying to take advantage of multicore CPUs.