2009-04-15 11 views
109

Comment la mise en correspondance de modèles dans Scala est-elle implémentée au niveau du bytecode? Est-ce comme une série de constructions if (x instanceof Foo), ou quelque chose d'autre?Comment la mise en correspondance de modèles dans Scala est-elle implémentée au niveau du bytecode?

Quelles sont ses implications en termes de performances? Par exemple, étant donné le code suivant (à partir de Scala By Example pages 46-48), à quoi ressemblerait le code Java équivalent pour la méthode eval?

abstract class Expr 
case class Number(n: Int) extends Expr 
case class Sum(e1: Expr, e2: Expr) extends Expr 

def eval(e: Expr): Int = e match { 
    case Number(x) => x 
    case Sum(l, r) => eval(l) + eval(r) 
} 

P.S. Je peux lire le bytecode Java, donc une représentation bytecode serait assez bonne pour moi, mais il serait probablement mieux pour les autres lecteurs de savoir à quoi cela ressemblerait en tant que code Java.

P.P.S. Est-ce que le livre Programming in Scala donne une réponse à cette question et à des questions similaires sur la façon dont Scala est mis en œuvre? J'ai commandé le livre, mais il n'est pas encore arrivé.

+0

Pourquoi ne pas simplement compiler l'exemple et le désassembler avec un désassembleur de bytecode Java? – Zifre

+0

Je vais probablement le faire, à moins que quelqu'un donne une bonne réponse en premier. Mais maintenant je veux dormir. ;) –

+22

La question est utile pour les autres lecteurs! – djondal

Répondre

85

Le faible niveau peut être exploré avec un désassembleur mais la réponse courte est que c'est un tas de si/elses où le prédicat dépend du modèle

case Sum(l,r) // instance of check followed by fetching the two arguments and assigning to two variables l and r but see below about custom extractors 
case "hello" // equality check 
case _ : Foo // instance of check 
case x => // assignment to a fresh variable 
case _ => // do nothing, this is the tail else on the if/else 

Il y a beaucoup plus que vous pouvez faire avec des motifs comme ou des motifs et des combinaisons comme "case Foo (45, x)", mais généralement ce sont des extensions logiques de ce que je viens de décrire. Les patterns peuvent également avoir des gardes, qui sont des contraintes supplémentaires sur les prédicats. Il y a aussi des cas où le compilateur peut optimiser la correspondance de modèle, par exemple, lorsqu'il y a un certain chevauchement entre les cas, cela peut unir les choses un peu. Les modèles avancés et l'optimisation sont une zone active de travail dans le compilateur, donc ne soyez pas surpris si le code d'octets améliore considérablement ces règles de base dans les versions actuelles et futures de Scala. En plus de tout cela, vous pouvez écrire vos propres extracteurs personnalisés en plus ou à la place de ceux que Scala utilise par défaut pour les classes de cas. Si vous le faites, le coût de la correspondance de modèle est le coût de tout ce que fait l'extracteur. Un bon aperçu est trouvé dans http://lamp.epfl.ch/~emir/written/MatchingObjectsWithPatterns-TR.pdf

70

James (ci-dessus) a dit le meilleur. Cependant, si vous êtes curieux c'est toujours un bon exercice pour regarder le bytecode démonté. Vous pouvez également appeler scalac avec l'option -print, qui imprime votre programme avec toutes les fonctionnalités spécifiques à Scala supprimées. C'est essentiellement Java dans les vêtements de Scala. Voici la sortie scalac -print pertinente pour l'extrait de code que vous avez donné:

def eval(e: Expr): Int = { 
    <synthetic> val temp10: Expr = e; 
    if (temp10.$isInstanceOf[Number]()) 
    temp10.$asInstanceOf[Number]().n() 
    else 
    if (temp10.$isInstanceOf[Sum]()) 
     { 
     <synthetic> val temp13: Sum = temp10.$asInstanceOf[Sum](); 
     Main.this.eval(temp13.e1()).+(Main.this.eval(temp13.e2())) 
     } 
    else 
     throw new MatchError(temp10) 
}; 
28

Depuis la version 2.8, Scala a eu l'annotation @switch. L'objectif est de s'assurer que la correspondance de modèle sera compilée en tableswitch or lookupswitch au lieu de la série d'instructions conditionnelles if.

+5

quand choisir @switch plus régulier sinon? –