2

Going through some code, I came across the function

def sum(): Function2[Double, Double, Double] = 
  new Function2[Double, Double, Double] {
    def apply(t1: Double, t2: Double): Double = t1 + t2;
  }

How is this different than the much simpler declaration,

def sum(): Function2[Double, Double, Double] = _ + _

Is there any benefit to using the former approach?

arshajii
  • 127,459
  • 24
  • 238
  • 287
  • 2
    btw, the bytecode for this two versions differs slightly -- both of those functions were put in distinct objects, compiled and decoded with javap. Former is compiled down to a single (plus unboxed version) method. Later is compiled to combination of different arguments (e.g. (double, int), (double, long) ... (long, double), (long, Object) ... + curried methods + custom toString. – om-nom-nom Jun 25 '13 at 18:45
  • @om-nom-nom: `toString` is the same, isn't it? – senia Jun 25 '13 at 18:53
  • @senia yes, it opts to default implementation, but [exists in bytecode](https://gist.github.com/lazyval/6e73be1a7f9bcd507b5d). The correct term should be *overriden* (if it is not it wouldn't be in bytecode, right?) with dummy redirection to [Function2.toString](https://github.com/scala/scala/blob/v2.10.2/src/library/scala/Function2.scala#L56). – om-nom-nom Jun 25 '13 at 19:02
  • @om-nom-nom: since `Function0` is trait there should be generated implementations with such redirections. The only difference is in case of lambda this implementation is in class `AbstractFunction2` and in case of `new Function2` this implementation is in anonymous class. – senia Jun 25 '13 at 19:17

1 Answers1

2

Readability

Verbose version is very clear for Java-developers. It looks almost like an anonymous class in Java. So it could be used to explain lambdas. This is the only advantage of the verbose version.

REPL support

There is a little difference in REPL, but it's not something useful:

scala> new Function2[Double, Double, Double] {
     |   def apply(t1: Double, t2: Double): Double = t1 + t2;
     | }
res0: java.lang.Object with (Double, Double) => Double = <function2>

scala> val sum: (Double, Double) => Double = _ + _
sum: (Double, Double) => Double = <function2>

Bytecode size

In terms of jvm trait is an interface. When you create a class based on trait compiler generates methods with simple call of implementation from trait's companion object.

FunctionN traits up to two parameters are specialized on Int, Long and Double parameters, the result type parameter is additionally specialized on Unit, Float and Boolean. It means for Function2 you'll get 54 addition apply methods (named like apply$mcZDD$sp).

With lambda version you are using abstract class scala.runtime.AbstractFunction2. All methods are generated in this class. Overridden only those that are necessary. Class-file size for lambda (ClassName$anonfun$1.class) is 1012 bytes. Less than 1kB.

In case of new Function2 all methods are generated in anonymous class. Class-file size for anonymous class (ClassName$anon$1.class) is about 20 kB.

For Function3 (not specialized) difference is not so big: 1.1kB vs 1.7kB, but FunctionN with 0-2 arguments are the most common lambdas. Without AbstractFunctionN bytecode size would be huge.

Community
  • 1
  • 1
senia
  • 37,745
  • 4
  • 88
  • 129
  • So there is no difference between these alternatives? (Other than the readability factor of course) – arshajii Jun 25 '13 at 18:37
  • @arshajii - Correct - there is no difference. – Rex Kerr Jun 25 '13 at 18:45
  • @senia - Huh? They're both specialized. – Rex Kerr Jun 25 '13 at 19:02
  • @RexKerr: you are correct. Decompiled `Function2` version is frightening. – senia Jun 25 '13 at 19:07
  • 1
    @RexKerr: could you please add your answer with an explanation why `AbstractFunctionN` classes are exists? I think it will be useful. It definitely will be better answer than mine. – senia Jun 25 '13 at 19:24
  • @senia - I don't have time to write up an adequate answer. The brief version is so that all the specialized forwarding methods don't have to be regenerated for each closure; they have a home in `AbstractFunctionN` and will be included by inheritance (saves bytecode). – Rex Kerr Jun 25 '13 at 19:35
  • @RexKerr: so it's only about bytecode size and compilation time, not about performance? I'll try to add it to my answer. Thank you. – senia Jun 25 '13 at 19:39