How to Scala, Written by a Java: 1 – Functions

(Find these examples at my GitHub))
I, like many others breaking into Scala come from a Java background. I found one of the largest hurdles to using Scala effectively was getting used to functions as a data type: functions are first class citizens in Scala.

Functional Basics

Functions may be assigned to values and variables, passed into other functions as arguments, and returned by functions. Let’s take for instance a simple function which takes as arguments three integer arguments and returns a double representing the sum of the first two arguments divided by the third.

def foo(sum1: Int, sum2: Int, divisor: Int): Double={
  val sum = sum1 + sum2
  sum.toDouble / divisor.toDouble
}

1. Currying

Say, you have a need for a function which takes an int adds 3 to it then divides that sum by 4. at this point we could write a new function:

def plus3Over4AsNew(arg: Int): Double={
  val sum = arg + 3
  sum.toDouble / 4.0
}

But, this is very reminiscent of foo. Its the same code with immediate values in place of a couple variables. Luckily in Scala we can avoid the extra typing by creating a curried function.

If f(x, y, z) is a function, a currying of f would partially apply f over one or more of its arguments: g(x) = f(x, 3, 4) in our case. So, back to our example.

def plus3Over4(arg: Int): Double = foo(arg, 3, 4)

This helps us in a few ways. Most obviously, we save on typing. Secondly and more importantly, we save on maintenance; the curried function benefits from any optimizations or bug fixes applied to the first.

2. Functions as Arguments

Suppose you need to perform many different operations upon the result of summing two integers. Say, in one part of the application you need to divide the sum by 4 and in another part of the application you need to raise the result to the power of 4. We could write these as two separate functions:

def addThenDivideBy4AsNew(sum1: Int, sum2: Int): Double={
  val sum = sum1 + sum2
  sum / 4.0
}
def addThenPowerOf4AsNew(sum1: Int, sum2: Int): Double={
  val sum = sum1 + sum2
  Math.pow(sum, 4)
}

But, like before, there will be duplication of code. As both these functions sum the arguments before applying the final step. We can remove this duplication by having a function which takes the arguments to be summed and a third argument that is a function from Int to Double.

def addThen(sum1: Int, sum2: Int, function: (Int => Double)): Double={
  val sum = sum1 + sum2
  function(sum)
}
private def divideBy4(arg: Int): Double ={
  arg / 4.0
}
private def powerOf4(arg: Int): Double={
  Math.pow(arg, 4)
}
def addThenDivideBy4(sum1: Int, sum2: Int) =
  addThen(sum1, sum2, divideBy4)
def addThenPowerOf4(sum1: Int, sum2: Int) =
  addThen(sum1, sum2, powerOf4)

The syntax for declaring a function uses the ‘=>’ symbol (sometimes called ‘arrow’ or ‘missile’). To the left of the => lies the arguments. If using more than one argument, separate them by commas and surround the block by parentheses:

def multiArgumentFunctionFunction(
  multiArgumentFunction: ((Int, Int, Double) => Double)
): Double = 0.0

You can also pass in functions anonymously using the => operator and curly braces.

def addThenDouble(sum1: Int, sum2: Int): Double =
  addThen(sum1, sum2,
    {sum =>
      sum * 2.0
    }
  )

3. Functions as Values & Partially Applied Functions

This is pretty straight forward. You may assign functions to values (or variables) and pass them about your application:

val fourArgs: ((Int, Int, Int, Int) => Int) = {(a, b, c, d) =>
  a + b + c + d
}
def manyArgs(
  a: Int,
  b: Int,
  c: Int,
  d: Int,
  e: Int,
  f: Int,
  g: Int,
  h: Int,
  i: Int,
  j: Int
): Int={
  val first = fourArgs(a, b, c, d)
  
  val threeArgs: ((Int, Int, Int) => Int) = fourArgs(_, _, _, 4)
  val second = threeArgs(e,f,g)
  
  val twoArgs: ((Int, Int) => Int) = threeArgs(_, _, 0)
  val third = twoArgs(h, i)
  
  val oneArg: (Int => Int) = twoArgs(_, 7)
  val fourth = oneArg(j)
  
  fourArgs(first, second, third, fourth)
}

Scala makes extensive use of the ‘_’ operator. In this context, it implies the function is partially applied. A partially applied function is similar to currying. The difference is, a curried function returns the result of the underlying function where partial application provides another function object.

3. Closures

Again, functions are first-class citizens in Scala. This means they may be declared anywhere any other data type may be declared including within another function:

def function(): Int={
  def innerFunction() = 7
  innerFunction()
}

This will return 7. Like any other value, you may define a function using other values and variables in scope.

def function(): Int={
  var variable = 1
  def innerFunction(arg: Int): Int={
    val increment = variable * 2
    arg + increment
  }
  variable = innerFunction(variable)
  innerFunction(variable)
}

This will return 9.
The var variable is declared outside of innerFunction’s scope yet its able to be read and modified by innerFunction. This is similar to using final pointers in Java anonymous classes. But, what’s happening in Scala is the compiler sees a reference to variable but cannot find that reference in scope so it goes to the next scope up, function’s scope, and checks there, this continues until variable is found. All the references required by innerFunction is called the closure of innerFunction (think set theory). The compiler brings in what’s necessary and no more.
With closures, objects that are needed by multiple nested scopes can live on even after their enclosing scope is exited. As long as a function which closes over an object is still in play, that object is still in play.

Next we’ll take a look at how to create classes.

Advertisements