2009-04-15 13 views
109

In che modo la corrispondenza dei pattern in Scala è implementata a livello di byte?In che modo la corrispondenza dei pattern in Scala è implementata a livello di byte?

È come una serie di costrutti if (x instanceof Foo) o qualcos'altro? Quali sono le sue implicazioni in termini di prestazioni?

Ad esempio, dato il seguente codice (da Scala By Example pagine 46-48), come sarebbe l'equivalente codice Java per il metodo 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. Posso leggere il bytecode di Java, quindi una rappresentazione di bytecode sarebbe abbastanza buona per me, ma probabilmente sarebbe meglio per gli altri lettori sapere come sarebbe simile al codice Java.

P.P.S. Il libro Programming in Scala dà una risposta a questo e domande simili su come viene implementato Scala? Ho ordinato il libro, ma non è ancora arrivato.

+0

Perché non si compila semplicemente l'esempio e lo si disassembla con un disassemblatore bytecode Java? – Zifre

+0

Probabilmente lo farò, a meno che qualcuno non dia una buona risposta prima. Ma adesso voglio dormire un po '. ;) –

+22

La domanda è utile per altri lettori! – djondal

risposta

85

Il basso livello possono essere esplorati con un disassemblatore ma la risposta breve è che è un po 'di se/elses in cui il predicato dipende dal modello

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 

C'è molto di più che si può fare con modelli come o schemi e combinazioni come "case Foo (45, x)", ma in genere quelle sono solo estensioni logiche di ciò che ho appena descritto. I pattern possono anche avere guardie, che sono ulteriori vincoli sui predicati. Ci sono anche casi in cui il compilatore può ottimizzare la corrispondenza dei pattern, ad esempio, quando c'è qualche sovrapposizione tra i casi, potrebbe unire le cose un po '. I pattern e l'ottimizzazione avanzati sono un'area attiva di lavoro nel compilatore, quindi non sorprenderti se il codice byte migliora sostanzialmente rispetto a queste regole di base nelle versioni attuali e future di Scala.

Oltre a tutto ciò, è possibile scrivere i propri estrattori personalizzati in aggiunta o al posto di quelli predefiniti che Scala utilizza per le classi dei casi. Se lo fai, il costo della corrispondenza del modello è il costo di qualunque cosa faccia l'estrattore. Una buona panoramica si trova in http://lamp.epfl.ch/~emir/written/MatchingObjectsWithPatterns-TR.pdf

70

James (sopra) lo ha detto meglio. Tuttavia, se sei curioso è sempre un buon esercizio guardare il codice by smontato. Puoi anche invocare scalac con l'opzione -print, che stamperà il tuo programma con tutte le funzionalità specifiche di Scala rimosse. È fondamentalmente Java negli abiti di Scala. Ecco il relativo scalac -print uscita per il frammento di codice che hai dato:

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

A partire dalla versione 2.8, Scala ha avuto il @switch annotazione. L'obiettivo è garantire che la corrispondenza del modello venga compilata in tableswitch or lookupswitch anziché in serie di istruzioni condizionali if.

+5

quando scegliere @switch su normale se altro? –