Bagaimana pencocokan pola di Scala diimplementasikan pada tingkat bytecode?

123

Bagaimana pencocokan pola di Scala diimplementasikan pada tingkat bytecode?

Apakah itu seperti rangkaian if (x instanceof Foo)konstruksi, atau sesuatu yang lain? Apa implikasi kinerjanya?

Misalnya, dengan kode berikut (dari Scala By Example halaman 46-48), bagaimana kode Java yang setara untuk evalmetode tersebut akan terlihat?

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)
}

PS Saya dapat membaca bytecode Java, jadi representasi bytecode akan cukup baik untuk saya, tetapi mungkin akan lebih baik bagi pembaca lain untuk mengetahui bagaimana tampilannya sebagai kode Java.

PPS Apakah buku Programming in Scala memberikan jawaban atas pertanyaan ini dan pertanyaan serupa tentang bagaimana Scala diterapkan? Saya sudah pesan bukunya, tapi belum sampai.

Esko Luontola
sumber
Mengapa Anda tidak mengkompilasi contoh dan membongkar dengan Java bytecode disassembler?
Zifre
Saya mungkin akan melakukannya, kecuali seseorang memberikan jawaban yang baik terlebih dahulu. Tapi sekarang saya ingin tidur. ;)
Esko Luontola
27
Pertanyaan itu berguna bagi pembaca lain!
djondal
1
@djondal: cara terbaik untuk mengatakannya adalah dengan memberi suara
positif

Jawaban:

96

Level rendah dapat dieksplorasi dengan disassembler tetapi jawaban singkatnya adalah bahwa itu adalah sekelompok if / elses di mana predikatnya tergantung pada polanya

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

Masih banyak lagi yang dapat Anda lakukan dengan pola atau pola dan kombinasi seperti "case Foo (45, x)", tetapi umumnya itu hanya ekstensi logis dari apa yang baru saja saya jelaskan. Pola juga dapat memiliki penjaga, yang merupakan batasan tambahan pada predikat. Ada juga kasus di mana kompilator dapat mengoptimalkan pencocokan pola, misalnya ketika ada beberapa tumpang tindih antar kasus, kompilator mungkin akan sedikit menyatu. Pola dan pengoptimalan lanjutan adalah area kerja aktif dalam kompiler, jadi jangan heran jika kode byte meningkat secara substansial melebihi aturan dasar ini dalam versi Scala saat ini dan yang akan datang.

Selain semua itu, Anda dapat menulis ekstraktor kustom Anda sendiri sebagai tambahan atau sebagai pengganti ekstraktor default yang digunakan Scala untuk kelas kasus. Jika Anda melakukannya, maka biaya pencocokan pola adalah biaya apa pun yang dilakukan ekstraktor. Tinjauan yang bagus dapat ditemukan di http://lamp.epfl.ch/~emir/written/MatchingObjectsWithPatterns-TR.pdf

James Iry
sumber
Saya percaya ini adalah tautan terkini: infoscience.epfl.ch/record/98468/files/…
greenoldman
78

James (di atas) mengatakan yang terbaik. Namun, jika Anda penasaran, selalu merupakan latihan yang baik untuk melihat bytecode yang dibongkar. Anda juga dapat memanggil scalacdengan -printopsi, yang akan mencetak program Anda dengan semua fitur khusus Scala dihapus. Ini pada dasarnya Java dalam pakaian Scala. Berikut adalah hasil yang relevan scalac -printuntuk potongan kode yang Anda berikan:

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)
};
Jorge Ortiz
sumber
34

Sejak versi 2.8, Scala memiliki anotasi @switch . Tujuannya adalah untuk memastikan, bahwa pencocokan pola akan dikompilasi menjadi tableswitch atau lookupswitch alih-alih serangkaian ifpernyataan bersyarat .

om-nom-nom
sumber
6
kapan harus memilih @switch daripada reguler jika lain?
Aravind Yarram
2
menggunakan @switchlebih efisien daripada pencocokan pola biasa. jadi jika semua kasus berisi nilai konstan, Anda harus selalu menggunakan @switch(karena implementasi bytecode akan sama dengan java, switchbukan banyak if-else)
lev