2010-08-27 5 views
23

Tôi có một số mã Scala mà sử dụng khá nhiều generics, và tôi đã lượm lặt từ các tài liệu sử dụng một biểu hiện trong các ràng buộc parametrization có thể giúp tôi làm việc xung quanh các vấn đề xóa loại (ví dụ như tôi muốn khởi tạo một đối tượng mới của loại chung). Chỉ, tôi muốn hiểu thêm về cách thức hoạt động của nó. Nó gần như cảm thấy như một số loại hashmap đó là nhận được một mục nhập cho mỗi trang web invocation ... Bất cứ ai ở đây có thể xây dựng?Scala's (2.8) Manifest hoạt động như thế nào?

class Image[T <: Pixel[T] : Manifest](fun() => T, size: Array[Int], data: Array[T]) { 
    def this(fun:() => T, size: Array[T]) { 
     this(fun, size, new Array[T](size(0) * size(1)); 
    } 
} 

Đây là điều mà dường như không được đề cập trong một trong các tài liệu mà tôi tìm thấy trên trang web, và trên Google I chủ yếu được bài đăng cũ hơn có cú pháp rất khác nhau, và từ 2.8 dường như có nhiều thứ đã thay đổi, tôi không chắc những thứ đó vẫn chính xác.

+0

thể trùng lặp của (http://stackoverflow.com/ question/3213510/what-is-a-manifest-in-scala-và-khi-do-bạn-cần-nó) –

+1

Tôi không chắc chắn rằng những câu trả lời thực sự trả lời nó hoạt động như thế nào, mặc dù? Ít nhất nó có thể rõ ràng hơn. Có phải điểm mà trình biên dịch thêm một đối số bổ sung vào các phương thức/hàm để giữ lớp bê tông cho tham số chung không? Và toán tử <: djc

+0

Hãy xem tại đây: http://debasishg.blogspot.com/2010/08/using-generalized-type-constraints-how.html –

Trả lời

35

Nó được một lúc kể từ khi tôi đào thông qua các mã nguồn cho Scala trong một nhiệm vụ để trả lời cùng một câu hỏi ... nhưng câu trả lời ngắn, khi tôi gọi lại -

Manifest là một cheat code để cho phép trình biên dịch để nhận được xung quanh loại tẩy xoá (nó không được sử dụng trong thời gian chạy). Nó gây ra nhiều đường dẫn mã được tạo ra tại thời gian biên dịch cho các kiểu đầu vào có thể khớp với tệp kê khai.

Tệp kê khai được giải quyết hoàn toàn, nhưng nếu có bất kỳ sự mơ hồ nào tại thời gian biên dịch về loại Manifest thì trình biên dịch sẽ dừng lại.

Với bản sao của Manifest bạn có một vài thứ có sẵn. Những điều chính bạn thường muốn có thể là từ java.lang.Class đã được xoá hoàn toàn qua erasure:

class BoundedManifest[T <: Any : Manifest](value: T) { 
    val m = manifest[T] 
    m.erasure.toString match { 
    case "class java.lang.String" => println("String") 
    case "double" | "int" => println("Numeric value.") 
    case x => println("WTF is a '%s'?".format(x)) 
    } 
} 

class ImplicitManifest[T <: Any](value: T)(implicit m: Manifest[T]) { 
    m.erasure.toString match { 
    case "class java.lang.String" => println("String") 
    case "double" | "int" => println("Numeric value.") 
    case x => println("WTF is a '%s'?".format(x)) 
    } 
} 

new BoundedManifest("Foo Bar!") 
// String 
new BoundedManifest(5) 
// Numeric value. 
new BoundedManifest(5.2) 
// Numeric value. 
new BoundedManifest(BigDecimal("8.62234525")) 
// WTF is a 'class scala.math.BigDecimal'? 
new ImplicitManifest("Foo Bar!") 
// String 
new ImplicitManifest(5) 
// Numeric value. 
new ImplicitManifest(5.2) 
// Numeric value. 
new ImplicitManifest(BigDecimal("8.62234525")) 
// WTF is a 'class scala.math.BigDecimal'? 

Đây là một ví dụ khá lung lay nhưng cho thấy những gì đang xảy ra. Tôi chạy nó cho đầu ra cũng như FWIW trên Scala 2.8.

Ranh giới [T ... : Manifest] là mới trong Scala 2.8 ... bạn thường phải nắm rõ tệp kê khai như được hiển thị trong ImplicitManifest. Bạn không thực sự GET một bản sao của Manifest. Nhưng bạn có thể tìm nạp một mã bên trong mã của mình bằng cách nói val m = manifest[T] ... manifest[_] được xác định trên Predef và có thể sẽ tìm thấy loại tệp kê khai phù hợp bên trong một khối liên kết.

Hai mục chính khác mà bạn nhận được từ một số Manifest<:<>:> kiểm tra loại phụ/siêu mẫu của một tệp kê khai so với loại khác. Nếu tôi nhớ lại một cách chính xác thì việc triển khai VERY vô cùng khôn ngoan và không phải lúc nào cũng phù hợp nhưng tôi có một nhóm mã sản xuất sử dụng chúng để kiểm tra một vài đầu vào có thể bị xóa.Một ví dụ đơn giản kiểm tra đối với biểu hiện khác:

class BoundedManifestCheck[T <: Any : Manifest](value: T) { 
    val m = manifest[T] 
    if (m <:< manifest[AnyVal]) { 
    println("AnyVal (primitive)") 
    } else if (m <:< manifest[AnyRef]) { 
    println("AnyRef") 
    } else { 
    println("Not sure what the base type of manifest '%s' is.".format(m.erasure)) 
    } 
} 


new BoundedManifestCheck("Foo Bar!") 
// AnyRef 
new BoundedManifestCheck(5) 
// AnyVal (primitive) 
new BoundedManifestCheck(5.2)  
// AnyVal (primitive) 
new BoundedManifestCheck(BigDecimal("8.62234525")) 
// AnyRef 

Jorge Ortiz có một bài đăng blog tuyệt vời (mặc dù cũ) về vấn đề này: http://www.scala-blogs.org/2008/10/manifests-reified-types.html

EDIT:

Bạn thực sự có thể nhìn thấy những gì Scala làm bằng cách yêu cầu nó in ra các kết quả của giai đoạn biên dịch xóa.

Chạy, trên ví dụ cuối cùng của tôi ở trên scala -Xprint:erasure test.scala tạo ra kết quả sau: [? Là gì Manifest tại Scala và khi nào bạn cần nó]

final class Main extends java.lang.Object with ScalaObject { 
    def this(): object Main = { 
    Main.super.this(); 
    () 
    }; 
    def main(argv: Array[java.lang.String]): Unit = { 
    val args: Array[java.lang.String] = argv; 
    { 
     final class $anon extends java.lang.Object { 
     def this(): anonymous class $anon = { 
      $anon.super.this(); 
     () 
     }; 
     class BoundedManifestCheck extends java.lang.Object with ScalaObject { 
      <paramaccessor> private[this] val value: java.lang.Object = _; 
      implicit <paramaccessor> private[this] val evidence$1: scala.reflect.Manifest = _; 
      def this($outer: anonymous class $anon, value: java.lang.Object, evidence$1: scala.reflect.Manifest): BoundedManifestCheck = { 
      BoundedManifestCheck.super.this(); 
      () 
      }; 
      private[this] val m: scala.reflect.Manifest = scala.this.Predef.manifest(BoundedManifestCheck.this.evidence$1); 
      <stable> <accessor> def m(): scala.reflect.Manifest = BoundedManifestCheck.this.m; 
      if (BoundedManifestCheck.this.m().<:<(scala.this.Predef.manifest(reflect.this.Manifest.AnyVal()))) 
      scala.this.Predef.println("AnyVal (primitive)") 
      else 
      if (BoundedManifestCheck.this.m().<:<(scala.this.Predef.manifest(reflect.this.Manifest.Object()))) 
       scala.this.Predef.println("AnyRef") 
      else 
       scala.this.Predef.println(scala.this.Predef.augmentString("Not sure what the base type of manifest '%s' is.").format(scala.this.Predef.genericWrapArray(Array[java.lang.Object]{BoundedManifestCheck.this.m().erasure()}))); 
      protected <synthetic> <paramaccessor> val $outer: anonymous class $anon = _; 
      <synthetic> <stable> def Main$$anon$BoundedManifestCheck$$$outer(): anonymous class $anon = BoundedManifestCheck.this.$outer 
     }; 
     new BoundedManifestCheck($anon.this, "Foo Bar!", reflect.this.Manifest.classType(classOf[java.lang.String])); 
     new BoundedManifestCheck($anon.this, scala.Int.box(5), reflect.this.Manifest.Int()); 
     new BoundedManifestCheck($anon.this, scala.Double.box(5.2), reflect.this.Manifest.Double()); 
     new BoundedManifestCheck($anon.this, scala.package.BigDecimal().apply("8.62234525"), reflect.this.Manifest.classType(classOf[scala.math.BigDecimal])) 
     }; 
     { 
     new anonymous class $anon(); 
     () 
     } 
    } 
    } 
} 
+1

Cũng đáng chú ý, việc loại bỏ ': Manifest' từ ví dụ cuối cùng gây ra lỗi biên dịch -' error: không thể tìm thấy giá trị ngầm cho tham số m: Manifest [T] 'khi bạn gọi' val m = manifest [T] '. Ranh giới ngữ cảnh cung cấp thông tin mà lệnh 'manifest [_]' cần phải hoạt động, vì vậy nó không phải là tùy chọn. –

4

"Ngữ cảnh bị ràng buộc" T ... : Manifest là đường cú pháp cho đối số ngầm định: (implicit man: Manifest[T]). Do đó tại thời điểm khởi tạo của hàm tạo kiểu được chỉ định bởi class Image, trình biên dịch tìm/cung cấp Tệp kê khai cho loại thực tế được sử dụng cho tham số kiểu T và giá trị đó "gắn với" thể hiện lớp kết quả trong suốt sự tồn tại của nó và "neo" ví dụ cụ thể của Image[Something] đối với Tệp kê khai cho số T.

+0

Cảm ơn, điều đó sẽ giúp, mặc dù tôi muốn biết giá trị gắn với cá thể như thế nào, tức là nơi nó được lưu. – djc

+0

Giống như bất kỳ tham số hàm tạo nào (được tham chiếu bên ngoài hàm tạo): Trong một trường. –

+0

đã chỉnh sửa câu trả lời của tôi để bao gồm đầu ra của giai đoạn xóa. Bạn có thể thực sự có Scalac hiển thị cho bạn những gì nó tạo ra bằng cách chuyển đối số '-Xprint: erasure' tới trình biên dịch/thời gian chạy. Điều này in ra trạng thái của mã Scala của bạn sau khi giai đoạn xóa được chạy. –