Manifest ใน Scala คืออะไรและเมื่อใดที่คุณต้องการ


133

เนื่องจาก Scala 2.7.2 มีสิ่งที่เรียกว่าManifestซึ่งเป็นวิธีแก้ปัญหาสำหรับการลบประเภทของ Java แต่Manifestทำงานอย่างไรและทำไม / เมื่อใดที่คุณต้องใช้?

โพสต์บล็อกแสดงออก: ประเภท reifiedโดย Jorge ออร์ติซอธิบายบางส่วนของมัน แต่มันก็ไม่ได้อธิบายวิธีการใช้งานร่วมกับขอบเขตบริบท

นอกจากนี้อะไรคือClassManifestความแตกต่างกับManifestอะไร?

ฉันมีรหัสบางส่วน (เป็นส่วนหนึ่งของโปรแกรมที่ใหญ่กว่าไม่สามารถรวมไว้ที่นี่ได้อย่างง่ายดาย) ซึ่งมีคำเตือนเกี่ยวกับการลบประเภท ฉันสงสัยว่าฉันสามารถแก้ปัญหาเหล่านี้ได้โดยใช้ไฟล์ Manifest แต่ฉันไม่แน่ใจว่าอย่างไร


2
มีการอภิปรายในรายชื่อผู้รับจดหมายเกี่ยวกับความแตกต่างของ Manifest / ClassManifest โปรดดูscala-programming-language.1934581.n4.nabble.com/…
Arjan Blokzijl

คำตอบ:


198

คอมไพลเลอร์รู้ข้อมูลเกี่ยวกับชนิดมากกว่าที่รันไทม์ JVM สามารถแสดงได้อย่างง่ายดาย Manifest เป็นวิธีที่คอมไพลเลอร์ส่งข้อความระหว่างมิติไปยังโค้ดที่รันไทม์เกี่ยวกับข้อมูลชนิดที่สูญหาย

สิ่งนี้คล้ายกับวิธีที่ชาวคลีปตันทิ้งข้อความที่เข้ารหัสไว้ในบันทึกฟอสซิลและดีเอ็นเอของมนุษย์ "ขยะ" เนื่องจากข้อ จำกัด ของสนามเรโซแนนซ์ความเร็วแสงและความโน้มถ่วงจึงไม่สามารถสื่อสารโดยตรงได้ แต่ถ้าคุณรู้วิธีปรับแต่งสัญญาณของพวกเขาคุณจะได้รับประโยชน์ในรูปแบบที่คุณคาดไม่ถึงตั้งแต่การตัดสินใจว่าจะกินอะไรเป็นอาหารกลางวันหรือจะเล่นล็อตโต้หมายเลขใด

ไม่ชัดเจนว่าไฟล์ Manifest จะเป็นประโยชน์ต่อข้อผิดพลาดที่คุณเห็นโดยไม่ทราบรายละเอียดเพิ่มเติมหรือไม่

การใช้ Manifests ทั่วไปอย่างหนึ่งคือการทำให้โค้ดของคุณทำงานแตกต่างกันไปตามประเภทคงที่ของคอลเล็กชัน ตัวอย่างเช่นถ้าคุณต้องการให้ List [String] แตกต่างจาก List ประเภทอื่น ๆ :

 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

วิธีการแก้ปัญหาที่อิงตามการสะท้อนนี้อาจเกี่ยวข้องกับการตรวจสอบแต่ละองค์ประกอบของรายการ

บริบทที่ถูกผูกไว้นั้นเหมาะสมที่สุดสำหรับการใช้ type-class ใน scala และ Debasish Ghosh ได้อธิบายไว้ที่นี่: http://debasishg.blogspot.com/2010/06/scala-implicits-type-classes-here-i.html

ขอบเขตบริบทสามารถทำให้ลายเซ็นของวิธีการอ่านง่ายขึ้น ตัวอย่างเช่นฟังก์ชันข้างต้นสามารถเขียนใหม่ได้โดยใช้ขอบเขตบริบทดังนี้:

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

25

ไม่ใช่คำตอบที่สมบูรณ์ แต่เกี่ยวกับความแตกต่างระหว่างManifestและClassManifestคุณสามารถดูตัวอย่างได้ในกระดาษScala 2.8Array :

คำถามเดียวที่เหลือคือวิธีใช้การสร้างอาร์เรย์ทั่วไป ต่างจาก Java ตรงที่ Scala อนุญาตให้สร้างอินสแตนซ์ใหม่ Array[T]โดยที่Tเป็นพารามิเตอร์ type สิ่งนี้สามารถนำไปใช้ได้อย่างไรเนื่องจากข้อเท็จจริงที่ว่าไม่มีการแสดงอาร์เรย์แบบสม่ำเสมอใน Java

Tวิธีเดียวที่จะทำเช่นนี้คือการต้องการข้อมูลเพิ่มเติมรันไทม์ซึ่งอธิบายชนิด Scala 2.8 มีกลไกใหม่นี้ซึ่งเรียกว่าManifest ประเภทของวัตถุให้ข้อมูลที่สมบูรณ์เกี่ยวกับชนิดManifest[T] โดยทั่วไปค่าจะถูกส่งผ่านในพารามิเตอร์โดยนัย และคอมไพเลอร์รู้วิธีที่จะสร้างให้พวกเขาชนิดที่รู้จักกันแบบคงที่T
ManifestT

มีอยู่นอกจากนี้ยังมีรูปแบบที่อ่อนแอชื่อซึ่งจะถูกสร้างขึ้นจากการรู้เพียงชั้นระดับสูงสุดของประเภทโดยไม่จำเป็นต้องรู้ทุกประเภทอาร์กิวเมนต์ClassManifest
เป็นข้อมูลรันไทม์ประเภทนี้ที่จำเป็นสำหรับการสร้างอาร์เรย์

ตัวอย่าง:

เราจำเป็นต้องให้ข้อมูลนี้โดยส่งผ่าน a ClassManifest[T]เข้าไปใน method เป็นพารามิเตอร์โดยปริยาย:

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 
} 

ในรูปแบบชวเลขสามารถใช้บริบท bound1 กับพารามิเตอร์ type Tแทน,

(ดูภาพประกอบ SO คำถามนี้)

, การให้:

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 
} 

เมื่อเรียกใช้ tabulate ในประเภทเช่นIntหรือStringหรือList[T]คอมไพเลอร์ Scala สามารถสร้างรายการคลาสเพื่อส่งผ่านเป็นอาร์กิวเมนต์โดยนัยเพื่อจัดทำตาราง


25

Manifest มีจุดมุ่งหมายเพื่อสร้างประเภททั่วไปที่ถูกลบออกเพื่อรันบน JVM (ซึ่งไม่รองรับ generics) อย่างไรก็ตามพวกเขามีปัญหาร้ายแรงบางประการ: พวกมันเรียบง่ายเกินไปและไม่สามารถรองรับระบบประเภทของ Scala ได้อย่างเต็มที่ ดังนั้นจึงเลิกใช้งานใน Scala 2.10 และถูกแทนที่ด้วยTypeTags (ซึ่งโดยพื้นฐานแล้วคอมไพเลอร์ Scala เองใช้เพื่อแสดงประเภทดังนั้นจึงสนับสนุนประเภท Scala อย่างเต็มที่) สำหรับรายละเอียดเพิ่มเติมเกี่ยวกับความแตกต่างโปรดดู:

กล่าวอีกนัยหนึ่ง

เมื่อไหร่ที่คุณต้องการ?

ก่อนที่จะ 2013/01/04, เมื่อ Scala 2.10 ได้รับการปล่อยตัว


ยังไม่เลิกใช้งาน (แต่จะเป็น) เนื่องจากการสะท้อนของ Scala ยังคงทดลองใน 2.10
Keros

ก่อนวันที่ 2013-01-04 หรือหากคุณใช้ API ที่ต้องใช้
David Moles

1

Let 's ยัง chck ออกmanifestในscalaแหล่งที่มา ( Manifest.scala) เราจะเห็น:

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

ดังนั้นเกี่ยวกับโค้ดตัวอย่างต่อไปนี้:

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

เราจะเห็นว่าmanifest functionการค้นหานัยm: Manifest[T]ที่พึงพอใจที่คุณให้ในรหัสตัวอย่างของเรามันเป็นtype parameter manifest[String]ดังนั้นเมื่อคุณเรียกสิ่งที่ชอบ:

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

คุณกำลังตรวจสอบว่ากระแสimplicit mที่คุณกำหนดในฟังก์ชันของคุณเป็นประเภทหรือไม่manifest[String]และเนื่องจากmanifestเป็นฟังก์ชันประเภทmanifest[T]จึงจะค้นหาเฉพาะmanifest[String]และจะพบว่ามีนัยดังกล่าวหรือไม่

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.