How Function2 works?


#1

When we define function literals, like :

(a: Int,b: Int) => a < b

Under the hood, this happens:

new Function2[Int, Int, Boolean] {
def apply(a: Int, b: Int) = a < b
}

Now, if I define multiple such function literals but with different type of arguments and return types, then does it mean I have multiple classes with name Function2?


#2

No. You are creating new instances. So even if you declare functions with the same signature (parameter and return types), each declaration is a new instance of FunctionX.


#3

But if parameters are of different types, then doesn’t it mean instances are of different type.Just that their name is same.
I think I am not able to put my thought correctly.


#4

You are nearly there. For example:

val func1 = new Function2[Int, Int, Boolean] {
  def apply(a: Int, b: Int) = a < b
}  

val func2 = new Function2[Int, Int, Boolean] {
  def apply(a: Int, b: Int) = a > b
}  

val func3 = new Function2[Int, Int, Int] {
  def apply(a: Int, b: Int) = a + b
}  

In the example above func1 and func2 have the same type. They are both Function2[Int, Int, Boolean]. Nevertheless they are different instances (objects). func1 is an object and ‘func2’ another.

In the example above func1 and func3 have different types. They both have two input parameters of type Int, however one returns a Boolean and the other an Int.

Note that Function2 is nothing more than a parametrized class. This class and its type parameters defines the final type. A function is an instance of that “final” type.


#5
new Function2[Int, Int, Boolean] {
  def apply(a: Int, b: Int) = a < b
}

That is an anonymous class, just like any other anonymous class. Compare that with following Java code:

new BiFunction<Integer, Integer, Boolean> {
  Boolean apply(Integer a, Integer b) {
    return a < b;
  }
}

or the lambda version:

BiFunction<Integer, Integer, Boolean> function = (a, b) -> a < b;

#6

Yes every function instance requires a new anonymous class. However since 2.12 no extra class files have to be created for these classes. Java 8 allows these classes to be generated “dynamically”.


#7

Now I am the one that is lost.

Is my explanation of class versus instance still correct and the classes referred to above “mere” details of the inner workings of Scala/JVM?


#8

Scala’s function syntax is a syntactic sugar translating to inheritance from FunctionX classes. It’s not an JVM detail as classes are also present in Scala Native and Scala.js - desugaring works the same there. There’s no need to treat functions as something special aside from the convenient syntactic sugar. The only possible semantic difference is the meaning of this or return inside lambdas.


#9

I think you are okay. An instance of the anonymous class is still an instance of its parent class.


#10

@hmf Yes, it still does.

The inner works are something like that:
The example you gave, the way you instantiated it, it’s called an anonymous class, so for each of them that computes something different you need to have another instance. Anonymous classes does not need to be extended to be used, they are stated and initiated on declaration just like a new Function2[T1, T2, R]...., all those things like collection.map(that => toThis(that)) are syntax sugar over anonymous classes, the advantage on JDK 8 is that they are created on the fly, rather the needing of a dumb class file generated extending it. but all that is the inner works


#11

@tarsa, @jarrodwb and @omgitsjoao

Appreciate the feedback. Learned something new.


#12

If you’d like to understand more about the JVM implementation details, hopefully this article will be helpful:
http://www.logicbig.com/tutorials/core-java-tutorial/java-8-enhancements/java-lambda-functional-aspect/