I’ve just spent some time learning about the difference between scala’s functions and methods. It’s a surprisingly complicated topic, so I’ll defer to the smart folks on stack overflow for the explanation itself:

Difference between method and function in Scala

Here are some interesting points / examples I took from that topic:

What is a function, and what is a method?

scala> def addOne(x:Int) = x + 1
addOne: (x: Int)Int

This is a method. If you try to reference it by name, it will be called without your consent1. You must use (addOne _) if you wish to capture a reference to it, as if you’re telling the compiler “no wait - don’t actually execute it yet!”. What the underscore is doing is telling the compiler to capture a Function object whose apply method calls the addOne method. A function object will not be called by default, so you get the strange behaviour:

val addOneFn = addOne _
addOneFn: (Int) => Int = <function1>

scala> addOneFn
res: (Int) => Int = <function1>

scala> addOne
<console> error: missing arguments for method addOne in object $iw;
follow this method with `_' if you want to treat it as a partially applied function

But strangely, using either form as an argument seems perfectly acceptable:

scala> List(1,2,3).map(addOneFn)
res: List[Int] = List(2, 3, 4)

scala> List(1,2,3).map(addOne)
res: List[Int] = List(2, 3, 4)

I cannot figure out why that second one works. Is it a special case? A fancy form of type inference? An implicit conversion somehow?

Note that the method can optionally be lifted when used as an argument, but the function cannot:

scala> List(1,2,3).map(addOne _)
res3: List[Int] = List(2, 3, 4)

scala> List(1,2,3).map(addOneFn _)
<console>: error: type mismatch;
found   : () => (Int) => Int
required: (Int) => ?

This indicates that you can lift a function again using _:

scala> (addOneFn _)  
res: () => (Int) => Int = <function0>

But you cannot use it directly:

scala> (addOneFn _)()   
<console> error: _ must follow method; cannot follow addOneFn.type

(I suspect that one is a bug)

And yet you can use it if you assign it to a variable first:

scala> val addOneFnLifted = addOneFn _
addOneFnLifted: () => (Int) => Int = <function0>

scala> addOneFnLifted()(1)
res: Int = 2

Defining functions directly

Moving on from that confusion, you can define a function directly instead of a method, if you like:

scala> val addOneFn = (x:Int) => x + 1
addOneFn: (Int) => Int = <function1>

Or, in shorthand:

scala> val addOneFn = (_:Int) + 1
addOneFn: (Int) => Int = <function1>

These have some pitfalls, though:

  • they are slightly less efficient in execution, as well as causing an extra object allocation
  • they cannot use generic argument types! For example:

     scala> def lst[A](args:A*) = List[A](args:_*)
     lst: [A](args: A*)List[A]
    	
     scala> val lstFn[A](args:A*) => List[A](args:_*)
     <console> error: '=' expected but '[' found.
    

…which begs the question, what happens if you lift a generified method into a function?

scala> lst _
res: (Nothing*) => List[Nothing] = <function1>

Ahh. I guess that was the best I could expect. At least you can choose your type:

scala> (lst[Int] _)       
res: (Int*) => List[Int] = <function1>

return

And finally: you cannot return from a function, only from a method. In fact, returning from a function that happens to be inside a method will do a non-local return!

scala> def upper(strs:Seq[String]):Unit = strs.foreach((s:String) => return s.toUpperCase())
upper: (strs: Seq[String])Unit

You can’t sneak that one past the compiler without admitting that your upper method returns Unit, although I can’t explain why this does not return a String, if it’s a non-local return. Interestingly, you get a misleading hint if you try to tell it to return a sequence:

scala> def upper(strs:Seq[String]):Seq[String] = strs.map((s:String) => return s.toUpperCase())
<console>: error: type mismatch;
found   : java.lang.String
required: Seq[String]
      def upper(strs:Seq[String]):Seq[String] = strs.map((s:String) => return s.toUpperCase())
                                                                                           ^

(changing the return type to String, as it seems to be suggesting, will not work)

In summary: the people that would have you believe that scala is no more complex than java are surely full of it. I like scala, but it’s clearly a more complex beast for all of its additional functionality and power. I don’t know why these corners of scala are so odd and contorted, but I can only assume it’s mostly for the sake of interfacing well with java or for running smoother (or faster) on the JVM. The fact that such concerns should sully an otherwise decent language is a shame indeed.

  1. A profoundly bad idea, in the opinion of myself and many others. Ruby makes the same mistake, and functional code suffers for it.