Why are functions values but methods are not, or what makes a function a value in particular? What would allow me to decipher that methods do not contain that characteristic?
Why are functions values but methods are not, or what makes a function a value in particular? What would allow me to decipher that methods do not contain that characteristic?
Scala is an object-oriented language. In object-oriented languages, every value is an object, and every object is a value.
Methods are bound to objects. It is awkward to have methods at the same time be a building block of objects and be objects themselves. It is easier to have them not be objects.
A function in Scala is, in some sense, just an object with an apply method. If there is no method named foo in scope, then foo() is simply syntactic sugar for foo.apply(). So, functions are values because they are objects.
While any object that has an apply method can be called as if it were a function, when we talk about "functions" in Scala, we usually mean something more specific: an instance of one of the FunctionN traits such as Function2[-T1, -T2, +R]. In particular, the function literal syntax
val add = (a: Int, b: Int) => a + b
val add = new Function2[Int, Int, Int] {
override def apply(a: Int, b: Int) = a + b
}
And the function type
type F = (Int, String, Long) => Boolean
is syntactic sugar for
type F = Function3[Int, String, Long, Boolean]
where each of the FunctionN traits is defined like this:
package scala
trait Function0[+R] {
def apply: R
override def toString = "<function>"
}
trait Function1[-T, +R] {
def apply(x: T): R
override def toString = "<function>"
}
trait Function2[-T1, -T2, +R] {
def apply(x1: T1, x2: T2): R
override def toString = "<function>"
}
trait Function3[-T1, -T2, -T3, +R] {
def apply(x1: T1, x2: T2, x3: T3): R
override def toString = "<function>"
}
and so on.
It is possible to convert a method into a function value using η-expansion. This can be done explicitly using a trailing underscore:
val f = println _
or in some cases, when it is clear that a function value is required, even by just using the bare name of the method:
val it = Iterable(1, 2, 3)
it.foreach(println)
Note that this is not much different from other languages. In Java, C#, and Ruby, for example, it is the same thing: methods are defined as part of classes (or structs in C# and modules in Ruby) and are bound to objects, but aren't objects themselves. Instead, you have a separate notion of a function (instance of a SAM interface / functional interface in Java, Action or Func in C#, Proc in Ruby), which is an object.
In Ruby, you can create a proxy object for a method that is bound to an object which has the same interface as Proc.
Other languages make different choices, i.e. in ECMAScript and Python, methods are objects / values, but they are not as tightly bound to objects as they are in Scala, Java, C#, and Ruby. Instead, methods are basically normal functions that are assigned to fields of the object.
One of the defining characteristics of a value is that it can be assigned to a variable. A method, technically speaking, cannot be assigned to a variable, for example
def m(i: Int): Int = i + 1
val f = m // error
However there is a process called eta expansion that converts a method to a function value. In Scala 2, we can trigger it by providing type ascription to a variable
val f: Int => Int = m
or by using underscore _
val f = m _
However this restriction has been lifted in Scala 3 (Dotty) which now provides automatic eta expansion so the following compiles successfully
scala> def f(i: Int): Int = i + 1
| val x = f
def f(i: Int): Int
val x: Int => Int = Lambda$1218/158882051@62cf6a84