3 votes

Pourquoi scalac génère des fermetures supplémentaires/envoloppantes

Premièrement. Considérez le code suivant

scala> val fail = (x: Any) => { throw new RuntimeException }
fail: Any => Nothing = 

scala> List(1).foreach(fail)
java.lang.RuntimeException
    at $anonfun$1.apply(:7)
    at $anonfun$1.apply(:7)
    at scala.collection.LinearSeqOptimized$class.foreach(LinearSeqOptimized.scala:59)

Il y a un anonfun supplémentaire entre foreach et l'exception. On s'attend à ce qu'il s'agisse d'une valeur de fail lui-même (objet d'une classe Function1[]), mais d'où vient le deuxième?

La signature de foreach prend cette fonction:

def foreach[U](f: A => U): Unit 

Alors, quel est le but du deuxième?

Deuxièmement, considérons le code suivant:

scala> def outer() {
     |   def innerFail(x: Any) = { throw new RuntimeException("inner fail") }
     | 
     |   Set(1) foreach innerFail
     | }
outer: ()Unit

scala> outer()
java.lang.RuntimeException: inner fail
    at .innerFail$1(:8)
    at $anonfun$outer$1.apply(:10)
    at $anonfun$outer$1.apply(:10)
    at scala.collection.immutable.Set$Set1.foreach(Set.scala:86)

Il y a deux anonfuns supplémentaires... sont-ils vraiment nécessaires? :-E

4voto

Rex Kerr Points 94401

Regardons le bytecode.

object ExtraClosure {
  val fail = (x: Any) => { throw new RuntimeException }
  List(1).foreach(fail)
}

Nous trouvons, à l'intérieur de la fonction anonyme (unique) :

public final scala.runtime.Nothing$ apply(java.lang.Object);
  Code:
   0:   new #15; //class java/lang/RuntimeException
   3:   dup
   4:   invokespecial   #19; //Method java/lang/RuntimeException."":()V
   7:   athrow

public final java.lang.Object apply(java.lang.Object);
  Code:
   0:   aload_0
   1:   aload_1
   2:   invokevirtual   #27; //Method apply:(Ljava/lang/Object;)Lscala/runtime/Nothing$;
   5:   athrow

En fait, ce n'est pas vraiment une fermeture supplémentaire. Nous avons une méthode surchargée avec deux valeurs de retour différentes (ce qui est tout à fait correct pour la JVM car elle traite le type de tous les paramètres comme faisant partie de la signature de la fonction). La fonction est générique, donc elle doit prendre l'objet de retour, mais le code que vous avez écrit renvoie spécifiquement Nothing, il crée également une méthode qui renvoie le type attendu.

Il existe différentes façons de contourner ce problème, mais aucune n'est sans défauts. C'est le type de chose que les JVM savent assez bien éliminer, cependant, donc je ne m'en ferais pas trop.

Édition : Et bien sûr, dans votre deuxième exemple, vous avez utilisé un def, et le anonfun est la classe qui encapsule ce def dans un objet fonction. C'est bien sûr nécessaire car foreach prend un Function1. Vous devez générer ce Function1 d'une manière ou d'une autre.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X