Scala Methods and Functions

It took me a bit of time to get my head around the difference between methods and functions in Scala. Methods are always defined using the “def” keyword, and they are always invoked on some object (either an object created using the “object” keyword or an object created by instantiating a class). Here are some examples:

object Obj {
  def add(x: Int, y: Int) = x + y
}

class C(val x: Int) {
  def subtract(y: Int) = x - y
}

Obj.add(5, 6) ===> 11
val c = new C(10)
c.subtract(3) ===> 7

Of course, you can import methods on an object to make them look like functions:

import Obj.add
add(7, 8) ===> 15

But, add() is really not a function. It’s still being invoked on object Obj. All we’ve done here is created a shortcut so that we don’t have to explicitly call it as Obj.add(). But the compiler will turn all our add()’s into Obj.add()’s under the covers.

Functions in Scala are defined as

{ (arguments) => expression }

(Technically, the braces are optional. You can also use parentheses or omit them altogether.) For example,

{ (x: Int, y: Int) => x + y }

You can assign functions to variables and then invoke them much like a method:

val add2 = { (x: Int, y: Int) => x + y }
add2(5, 6) ===> 11

There are a couple of key differences between functions and methods. First, unlike methods, functions are not invoked on an object. Functions are simply called and passed arguments, while methods are invoked on an object and passed arguments.

Second, unlike functions, methods cannot be assigned to variables. Nor can methods be passed as parameters to functions or other methods.

Converting methods into functions

The last point is key: Methods cannot be assigned to variables nor passed as parameters to functions or other methods. If you need to do this, you have to convert the method into a function. Consider our object c above which has method subtract(). One way to convert c.subtract into a function is as follows:

{ y: Int => c.subtract(y) }

We now have a function that takes a parameter y and passes it to the c.subtract() method. We can pass this new function to a method like map():

List(1, 2, 3) map { y: Int => c.subtract(y) } ===> List(9, 8, 7)

Or, we can assign this new function to a variable and then pass that variable to a method like map():

val subtractFromC = { y: Int => c.subtract(y) }
List(1, 2, 3) map subtractFromC ===> List(9, 8, 7)

But Scala also provides a shortcut for converting a method into a function. If you invoke the method on its object with no parentheses or parameters and follow it with an underscore, Scala will automatically convert it to a function for you. So, the following are all valid:

List(1, 2, 3) map c.subtract _    ===> List(9, 8, 7)
val subtractFromC = c.subtract _
List(1, 2, 3) map subtractFromC    ===> List(9, 8, 7)

In fact, if you use a method in a context where a function is expected, Scala will automatically convert the method into a function even if you don’t use the underscore:

List(1, 2, 3) map c.subtract    ===> List(9, 8, 7)
val subtractFromC: Int => Int = c.subtract
List(1, 2, 3) map subtractFromC    ===> List(9, 8, 7)

Note, though, that when we created subtractFromC, we had to explicitly specify the type for the variable. Otherwise, the compiler wouldn’t have enough context to automatically convert c.subtract into a function.

Composing functions

One nice trick is that you can use the operations “compose” and “andThen” to build bigger functions that chain together smaller functions. For example, suppose we have functions

val add5 = { x: Int => x + 5}
val times6 = { x: Int => x * 6 }

We can create a bigger function like this:

times6 compose add5

This new function takes an integer parameter, adds 5 to it, and then multiples the result by 6. We can do the same thing using andThen

add5 andThen times6

The only difference between andThen and compose is that the order of the functions is reversed. Use whichever makes more sense to you.

Since andThen and compose create new functions, we can pass them to other functions or methods. As an example, the following 3 expressions all produce the same result:

List(1, 2, 3) map { x => times6(add5(x)) }    ===> List(36, 42, 48)
List(1, 2, 3) map (add5 andThen times6)    ===> List(36, 42, 48)
List(1, 2, 3) map (times6 compose add5)    ===> List(36, 42, 48)

To me, the second two are easier to read than the first. The second two also let me avoid coming up with an artificial name for the parameter x.

Note that you can use andThen and compose with methods like c.subtract as well. You just have to convert it to a function first:

List(1, 2, 3) map (c.subtract _ andThen add5)    ===> List(14, 13, 12)

Note that if we swap the order, we don’t need the underscore since the compiler has enough context to automatically convert c.subtract to a function:

List(1, 2, 3) map (add5 andThen c.subtract)    ===> List(4, 3, 2)

Should we use methods at all?

In some ways, functions are nicer than methods. If you use functions, you never have to worry about underscores, and your code looks nicer. So, should you ever use methods at all? Consider the following traditionally-written class:

class D(var x: Int) {
  def add(y: Int) = x + y
  def set(y: Int) { x = y }
}

We have a class with a member variable x as well as methods that read x and modify x. We could just as easily write the same class using only functions:

class E(var x: Int) {
  val add = { y: Int => x + y }
  val set = { y: Int => x = y }
}

Our functions add() and set() are closures over the variable x. Therefore, add() and set() have full ability to read and modify x.

Let’s create an instance of E and play around with its functions:

val e = new E(10)    ===> e.x is 10
e.add(15)    ===> 25
e.set(1)    ===> e.x is 1
e.add(15)    ===> 16
List(1, 2, 3) foreach (e.add andThen e.set)   ===> e.x is 7

The last line is a bit confusing. The e.x value is initially 1. Then we call e.add(1) which results in 2. That 2 is then passed to e.set leaving e.x with a value of 2. Next we call e.add(2) which results in 4 being set into e.x since 2 + 2 is 4. Finally, we call e.add(3) which turns into 4 + 3, meaning 7 is set into e.x.

There are two key points here:

  1. The functions inside E look and behave almost exactly like methods.
  2. We don’t have to use any ugly underscores because we used functions instead of methods.

Suppose we did the same thing with class D (with methods instead of functions). Our code would look like this:

val d = new E(1)    ===> d.x is 1
List(1, 2, 3) foreach (d.add _ andThen d.set)   ===> d.x is 7

We get the same result, but the code is a bit uglier because of the underscore.

Note also that we can use inheritance and override functions. For example,

class F(x: Int) extends E(x) {
  override val set = { y: Int =>
    println("setting x = " + y)
    x = y
  }
}

So, should we use methods at all, or should we always use functions since they make the code a bit cleaner? It turns out that functions have at least 3 limitations that methods don’t have.

  1. You cannot provide default values for function parameters. For example, on our class C at the beginning of this blog post, we could have defined method subtract as “def subtract(x: Int = 1)”. That’s not possible if we had defined subtract as a function.
  2. Methods without parameters can be invoked without using parentheses. That’s not possible with functions. You always have to specify parentheses when invoking a function, even if it takes no parameters.
  3. With methods, when overriding a method in a parent class, you can access the parent’s version of the method by calling super.methodName. In functions, once you override a function in the parent class, there is no way to access the parent function. You’re stuck with only your overridden implementation.

To illustrate the third point, in our class F above, we cannot invoke super.set() inside the overridden set() function.

You have to weigh the pros and cons of both functions and methods when determining which to use. If a function takes no parameters, best to make it a method so that parentheses aren’t necessary. (Besides, in a purely functional sense, a parameterless function is only good for its side effects and probably should be avoided.) But if you’re likely to be composing a function with other functions, it’s probably better to write it as a function rather than a method.

Fun with arithmetic operations

Consider the arithmetic expression 5 + 10. The + is really a method on the value 5. So we could have written it as

(5).+(10)

(The parentheses around the 5 are necessary to keep Scala from getting confused and thinking that “5.” means the double value 5.0.)

But if that’s just a method call, then we should be able to put an underscore after (5 +) and use it like a function that adds 5 to an integer. Or, if we use it in a context where an integer function is expected, we don’t even need the underscore. This means we can do things like this:

List(1, 2, 3) map (5+)    ===> List(6, 7, 8)
List(1, 2, 3) map (5*)    ===> List(5, 10, 15)
List(1, 2, 3) map (5/)    ===> List(5, 2, 1)

There’s nothing really special about these. They just follow the rules we discussed above. But, using them creates a nice, brief, easy-to-read syntax for applying arithmetic operations to values.

Advertisements

3 Comments

Add yours →

  1. Thanks for this great article,

    However the examples aren’t working in Scala 2.11.2 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_25).
    To made the examples work I imported “language.postfixOps” to enable the postfix operators.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: