Mike Slinn

More Fun With Functions

— Draft —

Published 2014-01-13. Last modified 2024-08-12.
Time to read: 7 minutes.

This lecture continues from the Functions are First Class and Closures lectures. You should be comfortable with that material before working through this lecture. It shows how Function0, Function1 and lazily evaluated parameters are related. Currying and functional composition are also discussed.

This lecture is referred to by many other lectures in the Intermediate Scala course. You need to know this material well if you want to become a good Scala programmer.

The sample code for this lecture can be found in courseNotes/src/main/scala/Fun.scala.

Call By Name / Lazy Evaluation of a Parameter

So far we have only discussed eagerly evaluated parameters, which means the arguments are evaluated prior to passing the computed value into a method or FunctionN. For example, timidPi1 is a method that eagerly evaluates its value parameter.

Scala REPL
scala> import java.util.Calendar
import java.util.Calendar
scala>
val isWitchingHour: Boolean = Calendar.getInstance().get(Calendar.HOUR_OF_DAY) == 0 isWitchingHour: Boolean = false
scala>
def timidPi1(value: Int): String = if (!isWitchingHour) s"Eager evaluation yields $value" else "I am too scared to compute" timidPi1: (value: Int)String

Let’s invoke timidPi1 with a constant.

Scala REPL
scala> timidPi1(3)
res2: String = Eager evaluation yields 3 

Now let’s invoke timidPi1 with a stateful object that returns an Int.

Scala REPL
scala> object Stateful {
     |   var count = 0
     |   def increment = {
     |     count = count + 1
     |     count
     |   }
     | }
defined object Stateful
scala>
timidPi1(Stateful.increment) res3: String = Eager evaluation yields 1
scala> timidPi1(Stateful.increment) res4: String = Eager evaluation yields 2
scala>
timidPi1(Stateful.increment) res5: String = Eager evaluation yields 3

Each time timidPi1 is invoked with a stateful method call the state changes, which is why Stateful.increment computes a monotonically increasing value. This should not be surprising. If we reference the value parameter inside timidPi1a the same quantity is returned, which is also what we expect.

Scala REPL
scala> def timidPi1a(value: Int): String =
  if (!isWitchingHour) s"Eager evaluation yields $value, yes $value"
  else "I am too scared to compute"
timidPi1a: (value: Int)String
scala>
timidPi1a(3) res10: String = Eager evaluation yields 3, yes 3

You can defer evaluation of a parameter until it is referenced inside a method using call by name syntax, also known as lazy evaluation.

Scala REPL
scala> def timidPi2(value: => Int): String =
  if (!isWitchingHour) s"Lazy evaluation yields $value" else "I am too scared to compute"
timidPi2: (value: Int)String 

Notice how similar the above is to defining an eagerly evaluated parameter. The only difference is the addition of a rocket (right arrow) between the colon following the name of the variable and the variable type. Also notice that the resulting syntax almost looks like a function type without a parameter list. Let’s try invoking timidPi2 with a constant, and with the Stateful object.

Scala REPL
scala> timidPi2(3)
res7: String = Lazy evaluation yields 3
scala>
timidPi2(Stateful.increment) res8: String = Lazy evaluation yields 4
scala>
timidPi2(Stateful.increment) res9: String = Lazy evaluation yields 5

Again, not surprising. Now let’s modify timidPi2 so it references value twice.

Scala REPL
scala> def timidPi2a(value: => Int): String =
  if (!isWitchingHour) s"Lazy evaluation yields $value, yes $value"
  else "I am too scared to compute"
timidPi2a: (value: Int)String %

And we’ll run timidPi2a.

Scala REPL
scala> timidPi2a(3)
res11: String = Lazy evaluation yields 3, yes 3
scala>
timidPi2a(Stateful.increment) res12: String = Lazy evaluation yields 6, yes 7
scala>
timidPi2a(Stateful.increment) res13: String = Lazy evaluation yields 8, yes 9

So we see that value is evaluated each time it is mentioned – this is the big difference with lazy evaluation. Yes, evaluation is deferred until the value of value is required, but the evaluation happens afresh for each reference.

In contrast, here is a similar statement that accepts a no-argument Function, known as a Function0.

Scala REPL
scala> def timidPi3(value: () => Int): String =
  if (!isWitchingHour) s"Evaluating function yields ${value()}"
  else "I am too scared to compute"
timidPi3: (value: () => Int)String 

Let’s invoke timidPi3.

Scala REPL
scala> timidPi3(() => 3)
res17: String = Evaluating function yields 3
scala>
timidPi3(() => Stateful.increment) res18: String = Evaluating function yields 12
scala>
timidPi3(() => Stateful.increment) res19: String = Evaluating function yields 13

In this circumstance, Function0 provides a more awkward syntax than lazy evaluation, but identical semantics. Let’s try it with multiple references to value.

Scala REPL
scala> def timidPi3a(value: () => Int): String =
  if (!isWitchingHour) s"Evaluating function yields ${value()}, yes ${value()}"
  else "I am too scared to compute"
timidPi3a: (value: () => Int)String 

The results are again consistent with call by name / lazy evaluation semantics.

Scala REPL
scala> timidPi3a(() => 3)
res20: String = Evaluating function yields 3, yes 3
scala>
timidPi3a(() => Stateful.increment) res21: String = Evaluating function yields 14, yes 15
scala>
timidPi3a(() => Stateful.increment) res22: String = Evaluating function yields 16, yes 17

You can run this code by typing.

Shell
$ sbt "runMain LazyEvalLevel1"

Output is as shown above.

When writing code that invokes timidPi1 and timidPi3 there is no clue in the calling code if lazy or eager evaluation will be used. IntelliJ IDEA can highlight those for you if you enable Settings / Languages and Frameworks / Scala / Highlight arguments to by-name parameters, and the sub-selections Include block expressions and Include literals.

Here is a complete program that contrasts the syntax for eager evaluation, lazy evaluation and a no-arg function (Function0). These timidPi methods return BigDecimal instead of Int because Pi could be computed with arbitrarily large precision.

Scala code
object LazyEval extends App {
import java.util.Calendar
/** The Leibniz series algorithm converges slowly to Pi. About 5 billion iterations are required to yield accuracy to 10 decimal places. * Pi is the limit of this series: 4/1 - 4/3 + 4/5 - 4/7 + 4/9 ... * @see https://en.wikipedia.org/wiki/Leibniz_formula_for_%CF%80 */ def leibnizPi(iterationCount: Long=5000000000L, digits: Int=10): BigDecimal = { val numerator: BigDecimal = 4.0 var denominator: BigDecimal = 1 var plus = true var result: BigDecimal = 0.0 while (denominator < iterationCount) { if (plus) { result = result + numerator / denominator plus = false } else { result = result - numerator / denominator plus = true } denominator = denominator + 2 } result.setScale(digits, BigDecimal.RoundingMode.HALF_UP) }
val isWitchingHour: Boolean = Calendar.getInstance().get(Calendar.HOUR_OF_DAY) == 0 val scaredMsg = "I am too scared to compute"
def timidPi1(value: BigDecimal): String = if (!isWitchingHour) s"Eager evaluation yields $value" else scaredMsg def timidPi2(value: => BigDecimal): String = if (!isWitchingHour) s"Lazy evaluation yields $value" else scaredMsg def timidPi3(value: () => BigDecimal): String = if (!isWitchingHour) s"Evaluating function yields ${value()}" else scaredMsg
println(timidPi1(leibnizPi())) // eager evaluation println(timidPi2(leibnizPi())) // lazy evaluation println(timidPi3(() => leibnizPi())) // no-arg function }

You can run this program by typing.

Shell
$ sbt "runMain LazyEval"
Eager evaluation yields 3.1415926532
Lazy evaluation yields 3.1415926532
Evaluating function yields 3.1415926532 

Multiple Parameter Lists

Scala methods and functions can accept multiple parameter lists, a feature which is heavily used in functional programming, especially when working with libraries and frameworks. Let’s start with the simplest example, using the REPL.

Scala REPL
scala> def myFunc(x: Int)(y: Int): Int = x*x-y
myFunc: (x: Int)(y: Int)Int
scala>
val foo = myFunc(3)(4) foo: Int = 5

Yes, myFunc has two parameter lists, each of which happens to have a single parameter. You can have as many parameter lists as you want, but it is rare to have more than two. There are many reasons for using multiple parameter lists, which we will discuss in this lecture, and in several lectures of the Intermediate Scala course.

In the example above, myFunc is referred to as a curried function because it is actually implemented as a chain of two Function1 instances (single parameter functions). We’ll discuss currying in the Partially Applied Functions lecture of the Intermediate Scala course. The term has nothing to do with Asian cooking – instead, it is named for Haskell Curry the famous American mathematician and logician. Incidentally, Scala’s functional programming constructs were inspired by the pure functional language Haskell, also named for Haskell Curry.

Scala lets us replace parenthesis with curly braces around an argument list. These method invocations all produce the same result.

Scala code
myFunc(3)(4)
myFunc(3){4}
myFunc(3) { 4 }
myFunc(3) {
 4
}

Multiple argument lists give us a very nice looking block structure syntax when the parameter accepts a lazily evaluated block of code or a FunctionN. We can use this feature to easily create our own control structures. The With Pattern described later in this lecture is an excellent example of this.

Here are a couple of simple examples. You will learn more Scala programming techniques in the Intermediate Scala course which will allow you to construct more practical and complete designs with multiple parameter lists, and passing code blocks and functions as arguments.

Handmade unless Construct

Scala code
def unless(cond: Boolean)(body: => Unit): Unit = if (!cond) body

unless will only execute body if the predicate cond is false. The predicate is not lazily evaluated, which means it is evaluated when this method is invoked. What’s more, body is only evaluated if cond is false. Here’s an example of how it’s used.

Scala REPL
scala> var x = 1
x: Int = 1
scala>
unless(x == 0) { println(s"I can divide by x because x is not zero: ${3 / x}") } I can divide by x because x is not zero: 3
scala>
x = 0 x: Int = 0
scala>
unless(x == 0) { println(s"I can divide by x because x is not zero: ${3 / x}") }

The AntiPatterns: Eager Evaluation and Closing Over External State section of the Partially Applied Functions lecture discusses this concept further.

Handmade until Construct

until is the inverse of a while loop. It always executes body at least once, and it keeps invoking itself as long as the predicate cond is false. This means that both cond and body must be lazily evaluated, so their values are computed each time the method recursively invokes itself. If these parameters were eagerly evaluated, their values would be fixed on the first invocation and the program might never terminate, and probably would not produce correct results.

Scala code
import scala.annotation.tailrec

@tailrec
def until(body: => Unit)(cond: => Boolean): Unit = {
  body
  if (!cond) until(body)(cond)
}

Here’s an example of how to use until.

Scala code
var i = 0
until {
  println(s"$i squared = ${i * i}")
  i += 1
} (i == 10)

You can run these examples by typing:

Shell
$ sbt "runmain MultipleParamLists"
0 squared = 0
1 squared = 1
2 squared = 4
3 squared = 9
4 squared = 16
5 squared = 25
6 squared = 36
7 squared = 49
8 squared = 64
9 squared = 81 

“With” Pattern

The With Pattern is commonly used in many languages and frameworks. For example, Play Framework uses something similar for request handling in controllers.

Scala code
case class Blarg(i: Int, s: String)
def withBlarg(blarg: Blarg)(operation: Blarg => Unit): Unit = operation(blarg)

Walking through the code:

  1. An instance of Blarg gets passed into the withBlarg method as the only parameter in the first parameter list for that method.
  2. The second parameter list accepts one parameter, which is a function that receives a Blarg and returns Unit. In other words, the function is only useful for its side effects. BTW, the With Pattern does not require the function to return Unit.
  3. The body of withBlarg merely causes the operation passed in from the second parameter list to be invoked on the Blarg instance that was passed in from the first parameter list.

Let’s try out this code. Notice that we use squiggly braces to demark the second parameter list, because it is more legible.

Scala REPL
scala> withBlarg(Blarg(1, "blarg")) { blarg =>
     |   println(blarg)
     | }
Blarg(1,blarg) 

We can use shorthand syntax because there is only one reference to the Blarg in the lambda function.

Scala REPL
scala> withBlarg(Blarg(2, "asdf")) { println(_) }
Blarg(2,asdf) 

We can use an even terser shorthand syntax because println only receives one parameter.

Scala REPL
scala> withBlarg(Blarg(3, "qwer")) { println }
Blarg(3,qwer) 

The With pattern will be revisited in the ’With’ Pattern Using Implicits section of the Implicit Values lecture of the Intermediate Scala course, further developed in the ’With’ Pattern Revisited section of the Parametric Types lecture of the same course, enhanced yet again in the Parametric ’With’ Pattern Revisited section of the Partial Functions lecture and even further enhanced in the Structural Types With Parametrics section of the Structural Types lecture.

Setup

Lets define a trait and a few functions for use later in this lecture.

Here we see a trait that extends Function1[Int, Int], expressed with syntactic sugar (Int => Int). The trait defines two methods: apply, which is a default method and so is implicitly called if no method name is provided, and tilde (~), commonly used in Scala as a synonym for "and then". The use of andThen is a form of method composition, also known as method chaining. We will see how that works in a second.

Scala code
trait Fn extends (Int => Int) {
  def apply(x: Int): Int
def ~(f: => Fn) = this andThen f }
val addOne: Fn = new Fn { def apply(x: Int) = 1 + x }
val multiplyTwo: Fn = new Fn { def apply(x: Int) = 2 * x }

The trait Fn is abstract because the apply method is not implemented. addOne and multiplyTwo are instances of anonymous classes which implement the trait’s abstract apply method.

The Scala compiler can infer which constructor to invoke according to the declared type to be returned. This allows addOne and multiplyTwo to be defined without writing new Fn, like this.

Scala code
val addOne: Fn = (x: Int) => 1 + x
val multiplyTwo: Fn = (x: Int) => 2 * x

This works because Scala silently uses the defined type for functions (Fn) to call the constructor for that type.

If you do not declare the type then the Scala compiler defines the returned type to be Int => Int, which is not useful.

Bad Scala code
val addOneNG = (x: Int) => 1 + x
val multiplyTwoNG = (x: Int) => 2 * x
// Does not compile because addOneNG and multiplyTwoNG have type Function1[Int, Int], which does not define a method called ~
// addOneNG ~ multiplyTwoNG

Composition

Let’s use these definitions. First, let’s call each method separately.

Scala REPL
scala> addOne(2)
res14: Int = 3
scala>
multiplyTwo(4) res15: Int = 8

Next we can compose the two functions together, thereby defining a new instance of a new anonymous class, also with type Int => Int.

Scala REPL
scala> val compute = multiplyTwo ~ addOne
compute: Int => Int = <function1> 

Now we can invoke this new function by passing it an Int. Because the definition of ~ is for this (which is multiplyTwo) to be performed, andThen f to be performed (which is addOne), the argument is first doubled then one is added.

Scala REPL
scala> compute(2)
res16: Int = 5 

We can define another anonymous class, which defines a function that performs the operations in the opposite order, and invoke it all together, like this.

Scala REPL
scala> (addOne ~ multiplyTwo)(6)
res17: Int = 14 

Method Lifting Turns Varargs into a Sequence

Just as a reminder, repeated arguments (also known as varargs, discussed in the Classes lecture) are denoted by an asterisk, and the arguments are received as a Seq.

The following code defines a method foo that accepts one or more integers:

Scala REPL
scala> def foo(ns:Int*) = ns.sum
foo: (ns: Int*)Int
scala>
foo(1,2,3) res11: Int = 6

Notice what happens when we lift foo to a Function1.

Scala REPL
scala> val foov = foo _
foov: Seq[Int] => Int = <function1> 

The type of foov is Seq[Int] => Int. In other words, it is a Function1 that accepts a Seq[Int] and returns an Int. We can call it by supplying a sequence, which is just a list of Int.

Scala REPL
scala> foov(Seq(1,2,3))
res12: Int = 6 

We will discuss sequences and lists (and much more) in the Collections Overview lecture of the Intermediate Scala course.

Dispensing with App and DelayedInit

We first came across the App trait in the Hello, world! Exercise in the SBT Project Setup lecture. By now you know that App can be used to create a Scala console application. I did not mention at the time that App incorporates behavior from DelayedInit. This can cause problems when creating new instances of objects. The following Program abstract class is a drop-in replacement for App that does not use DelayedInit.

Scala code
package app
abstract class Program[U](body: => U) { def main(args: Array[String]): Unit = { body() } }
object MyThing extends Program({ println("hello world") })

Can you explain what this sample program does?


* indicates a required field.

Please select the following to receive Mike Slinn’s newsletter:

You can unsubscribe at any time by clicking the link in the footer of emails.

Mike Slinn uses Mailchimp as his marketing platform. By clicking below to subscribe, you acknowledge that your information will be transferred to Mailchimp for processing. Learn more about Mailchimp’s privacy practices.