2009-04-15 12 views
109

¿Cómo se implementa la coincidencia de patrones en Scala en el nivel de bytecode?¿Cómo se implementa la coincidencia de patrones en Scala en el nivel de bytecode?

¿Es como una serie de construcciones if (x instanceof Foo), o algo más? ¿Cuáles son sus implicaciones de rendimiento?

Por ejemplo, dado el siguiente código (desde Scala By Example páginas 46-48), ¿cómo sería el código equivalente de Java para el método 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. Puedo leer el bytecode de Java, por lo que una representación de bytecode sería lo suficientemente buena para mí, pero probablemente sería mejor para los otros lectores saber cómo se vería como código Java.

P.P.S. ¿Responde el libro Programming in Scala a esta y otras preguntas similares sobre cómo se implementa Scala? He ordenado el libro, pero aún no ha llegado.

+0

¿Por qué no compila el ejemplo y lo desmonta con un desensamblador de código de Java? – Zifre

+0

Probablemente lo haga, a menos que alguien dé una buena respuesta primero. Pero ahora quiero dormir un poco. ;) –

+22

¡La pregunta es útil para otros lectores! – djondal

Respuesta

85

El bajo nivel se pueden explorar con un desensamblador, pero la respuesta corta es que es un montón de si/vigilara donde el predicado depende del patrón

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 

Hay mucho más que se puede hacer con patrones como o patrones y combinaciones como "caso Foo (45, x)", pero generalmente esas son simplemente extensiones lógicas de lo que acabo de describir. Los patrones también pueden tener protecciones, que son restricciones adicionales en los predicados. También hay casos en que el compilador puede optimizar la coincidencia de patrones, por ejemplo, cuando hay una superposición entre los casos, puede unir las cosas un poco. Los patrones avanzados y la optimización son un área activa de trabajo en el compilador, por lo que no se sorprenda si el código de bytes mejora sustancialmente sobre estas reglas básicas en las versiones actuales y futuras de Scala.

Además de todo eso, puede escribir sus propios extractores personalizados además o en lugar de los predeterminados que Scala usa para las clases de casos. Si lo hace, entonces el costo de la coincidencia de patrón es el costo de lo que sea que haga el extractor. Se encuentra una buena descripción en http://lamp.epfl.ch/~emir/written/MatchingObjectsWithPatterns-TR.pdf

70

James (arriba) lo dijo mejor. Sin embargo, si tiene curiosidad, siempre es un buen ejercicio observar el bytecode desensamblado. También puede invocar scalac con la opción -print, que imprimirá su programa con todas las características específicas de Scala eliminadas. Es básicamente Java en la ropa de Scala. Aquí está la correspondiente scalac -print salida para el fragmento de código que diste:

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

Desde la versión 2.8, Scala ha tenido la anotación @switch. El objetivo es garantizar que la coincidencia de patrones se compile en tableswitch or lookupswitch en lugar de series de instrucciones condicionales if.

+5

cuándo elegir @muy más de lo normal si no? –

Cuestiones relacionadas