Scala is a multi-paradigm language which supports both object-oriented programming and functional programming. So Scala has both functions and objects. But in the implementation level, function values are treated as objects. The function type A => B is just an abbreviation for the class scala.Function1[A, B],
package scala trait Function1[A, B] { def apply(x: A): B }
There are traits Function2, …, Function22 for functions which take more arguments.
An anonymous function such as (x: Int) => x * x is expanded to
new Function1[Int, Int] { def apply(x: Int) = x * x }
A function call such as f(a, b) is expanded to f.apply(a, b).
So the translation of
val f = (x: Int) => x * x f(7)
would be
val f = new Function1[Int, Int] { def apply(x: Int) = x * x } f.apply(7)
This trick is necessary because JVM does not allow passing or returning functions as values. So to overcome the limitation of JVM (lack of higher order functions), Scala compiler wraps function values in objects.
The following method f is not itself a function value.
def f(x: Int): Boolean = ...
But when f is used in a place where a Function type is expected, it is converted automatically to the function value
(x: Int) => f(x)
This is an example of eta expansion.
The code examples in this article are taken from Martin Odersky’s Functional Programming Principles in Scala lecture.