Apa itu Manifest di Scala dan kapan Anda membutuhkannya?

132

Sejak Scala 2.7.2 ada sesuatu yang disebut Manifestyang merupakan solusi untuk penghapusan tipe Java. Tetapi bagaimana cara Manifestkerjanya secara tepat dan mengapa / kapan Anda perlu menggunakannya?

Posting blog Manifests: Reified Types oleh Jorge Ortiz menjelaskan beberapa di antaranya, tetapi tidak menjelaskan bagaimana menggunakannya bersama dengan batas konteks .

Juga, apa ClassManifest, dengan apa bedanya Manifest?

Saya memiliki beberapa kode (bagian dari program yang lebih besar, tidak dapat dengan mudah memasukkannya di sini) yang memiliki beberapa peringatan terkait dengan jenis penghapusan; Saya kira saya bisa menyelesaikan ini dengan menggunakan manifes, tapi saya tidak yakin bagaimana caranya.

Jesper
sumber
2
Telah ada diskusi di milis tentang perbedaan Manifest / ClassManifest, lihat scala-programming-language.1934581.n4.nabble.com/...
Arjan Blokzijl

Jawaban:

197

Kompiler mengetahui lebih banyak informasi tentang tipe daripada yang dapat dengan mudah direpresentasikan oleh runtime JVM. Manifest adalah cara bagi kompiler untuk mengirim pesan antar-dimensi ke kode pada saat runtime tentang jenis informasi yang hilang.

Ini mirip dengan bagaimana orang Kleptonia meninggalkan pesan yang disandikan dalam catatan fosil dan "sampah" DNA manusia. Karena keterbatasan kecepatan cahaya dan bidang resonansi gravitasi, mereka tidak dapat berkomunikasi secara langsung. Tetapi, jika Anda tahu cara menyesuaikan sinyal mereka, Anda dapat mengambil manfaat dengan cara yang tidak dapat Anda bayangkan, dari memutuskan apa yang akan dimakan untuk makan siang atau nomor lotre mana yang akan dimainkan.

Tidak jelas apakah Manifest akan menguntungkan kesalahan yang Anda lihat tanpa mengetahui lebih detail.

Salah satu penggunaan Manifest yang umum adalah membuat kode Anda berperilaku berbeda berdasarkan pada jenis statis koleksi. Misalnya, bagaimana jika Anda ingin memperlakukan Daftar [String] berbeda dari jenis Daftar lainnya:

 def foo[T](x: List[T])(implicit m: Manifest[T]) = {
    if (m <:< manifest[String])
      println("Hey, this list is full of strings")
    else
      println("Non-stringy list")
  }

  foo(List("one", "two")) // Hey, this list is full of strings
  foo(List(1, 2)) // Non-stringy list
  foo(List("one", 2)) // Non-stringy list

Solusi berbasis refleksi untuk ini mungkin akan melibatkan memeriksa setiap elemen daftar.

Batas konteks tampaknya paling cocok untuk menggunakan tipe-kelas dalam scala, dan dijelaskan dengan baik di sini oleh Debasish Ghosh: http://debasishg.blogspot.com/2010/06/scala-implicits-type-classes-here-i.html

Batas konteks juga bisa membuat metode tanda tangan lebih mudah dibaca. Misalnya, fungsi di atas dapat ditulis ulang menggunakan batas konteks seperti:

  def foo[T: Manifest](x: List[T]) = {
    if (manifest[T] <:< manifest[String])
      println("Hey, this list is full of strings")
    else
      println("Non-stringy list")
  }
Mitch Blevins
sumber
25

Bukan jawaban yang lengkap, tetapi mengenai perbedaan antara Manifestdan ClassManifest, Anda dapat menemukan contoh di makalah Scala 2.8Array :

Satu-satunya pertanyaan yang tersisa adalah bagaimana menerapkan pembuatan array generik. Tidak seperti Java, Scala memungkinkan sebuah contoh ciptaan baru Array[T]di mana Tadalah parameter tipe. Bagaimana ini bisa diimplementasikan, mengingat fakta bahwa tidak ada representasi array yang seragam di Jawa?

Satu-satunya cara untuk melakukan ini adalah memerlukan informasi runtime tambahan yang menjelaskan tipe tersebut T. Scala 2.8 memiliki mekanisme baru untuk ini, yang disebut Manifest . Objek tipe Manifest[T]memberikan informasi lengkap tentang tipe T.
Manifestnilai biasanya diteruskan dalam parameter implisit; dan kompiler tahu bagaimana membangunnya untuk tipe yang dikenal secara statis T.

Ada juga bentuk yang lebih lemah bernama ClassManifestyang dapat dibangun dari hanya mengetahui kelas tingkat atas dari suatu jenis, tanpa harus mengetahui semua jenis argumennya .
Ini adalah jenis informasi runtime yang diperlukan untuk pembuatan array.

Contoh:

Kita perlu memberikan informasi ini dengan memasukkan ClassManifest[T]metode ke dalam sebagai parameter implisit:

def  tabulate[T](len:Int,  f:Int=>T)(implicit m:ClassManifest[T]) =  { 
  val  xs  =  new  Array[T](len) 
  for   (i  <- 0  until   len)  xs(i)   = f(i) 
  xs 
} 

Sebagai bentuk steno, batas konteks1 dapat digunakan pada parameter tipe Tsebagai gantinya,

(Lihat pertanyaan SO untuk ilustrasi )

, memberi:

def  tabulate[T:    ClassManifest](len:Int,  f:Int=>T)  =  { 
  val  xs  =  new  Array[T](len) 
  for   (i  <- 0  until   len)  xs(i)   = f(i) 
  xs 
} 

Saat memanggil tabulasi pada jenis seperti Int, atau String, atau List[T], kompilator Scala dapat membuat manifes kelas untuk dilewati sebagai argumen implisit untuk ditabulasi.

VONC
sumber
25

Manifest dimaksudkan untuk memverifikasi tipe generik yang dapat dihapus tipe untuk dijalankan di JVM (yang tidak mendukung obat generik). Namun, mereka memiliki beberapa masalah serius: mereka terlalu sederhana, dan tidak dapat sepenuhnya mendukung sistem tipe Scala. Mereka kemudian ditinggalkan dalam Scala 2.10, dan diganti dengan TypeTags (yang pada dasarnya apa yang digunakan kompilator Scala sendiri untuk mewakili tipe, dan karenanya sepenuhnya mendukung tipe Scala). Untuk detail lebih lanjut tentang perbedaannya, lihat:

Dengan kata lain

kapan kamu membutuhkannya

Sebelum 2013-01-04, ketika Scala 2.10 dirilis .

Siput mekanik
sumber
Ini belum ditinggalkan (tetapi akan menjadi), karena refleksi Scala masih eksperimental pada 2.10.
Keros
Sebelum 2013-01-04, atau jika Anda menggunakan API yang bergantung padanya.
David Moles
1

Mari kita habiskan juga manifestdalam scalasumber ( Manifest.scala), kita melihat:

Manifest.scala:
def manifest[T](implicit m: Manifest[T])           = m

Jadi berkaitan dengan kode contoh berikut:

def foo[A](somelist: List[A])(implicit m: Manifest[A]): String = {
  if (m <:< manifest[String]) {
    "its a string"
  } else {
    "its not a string"
  }
}

kita dapat melihat bahwa manifest functionpencarian implisit m: Manifest[T]yang memuaskan type parameterAnda berikan dalam contoh kode kami itu manifest[String]. Jadi, ketika Anda memanggil sesuatu seperti:

if (m <:< manifest[String]) {

Anda memeriksa apakah arus implicit myang Anda tetapkan dalam fungsi Anda bertipe manifest[String]dan karena manifestmerupakan fungsi tipe, manifest[T]ia akan mencari yang spesifik manifest[String]dan akan menemukan jika ada yang tersirat.

Tomer Ben David
sumber