web-dev-qa-db-ja.com

Scalaのマニフェストとは何ですか、いつ必要ですか?

Scala 2.7.2以来、Javaの型消去の回避策であるManifestと呼ばれるものがあります。しかし、Manifestはどのように正確に機能しますか?それを使用する必要がありますか?

Jorge Ortizによるブログ投稿マニフェスト:Reified Typesはその一部を説明していますが、使用方法については説明していません context bounds と一緒に。

また、ClassManifestとは何ですか、Manifestとの違いは何ですか?

型の消去に関していくつかの警告があるコード(大きなプログラムの一部で、ここに簡単に含めることはできません)があります。私はマニフェストを使用してこれらを解決できると思いますが、どのように正確に定かではありません。

130
Jesper

コンパイラーは、JVMランタイムが簡単に表現できる以上の型に関する情報を知っています。マニフェストは、コンパイラが、実行時に失われた型情報に関する次元間メッセージをコードに送信する方法です。

これは、クレプトン人が化石記録と人間の「ジャンク」DNAにエンコードされたメッセージを残した方法に似ています。光速度と重力共鳴場の制限により、それらは直接通信できません。しかし、あなたが彼らの信号に合わせる方法を知っているなら、あなたは昼食に何を食べるか、どの宝くじ番号をプレーするかを決めることから、想像できない方法で利益を得ることができます。

マニフェストが、詳細を知らなくても表示されているエラーに利益をもたらすかどうかは明らかではありません。

マニフェストの一般的な使用法の1つは、コレクションの静的な型に基づいてコードの動作を変えることです。たとえば、List [String]を他のタイプのリストとは異なる方法で処理する場合はどうなりますか。

 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

これに対するリフレクションベースのソリューションには、おそらくリストの各要素の検査が含まれます。

コンテキストバウンドは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")
  }
194
Mitch Blevins

完全な答えではありませんが、ManifestClassManifestの違いについては、 Scala 2.8 Array paper で例を見つけることができます。

残っている唯一の質問は、汎用配列作成の実装方法です。 Javaとは異なり、Scalaはインスタンスの作成を許可します。新しいArray[T]で、Tは型パラメーターです。これは、均一な配列表現が存在しないという事実を考えると、どのように実装できますか? Java?

これを行う唯一の方法は、T型を記述する追加のランタイム情報を要求することです。 Scala 2.8には、Manifestと呼ばれる新しいメカニズムがあります。タイプManifest[T]のオブジェクトは完全な情報を提供しますタイプTについて。
Manifest値は通常、暗黙的なパラメーターで渡されます。コンパイラーは、静的に既知の型Tに対してそれらを構築する方法を知っています。

また、weaker formという名前がありますClassManifest型のレベルクラス。必ずしもすべての引数型を知っている必要はありません
配列の作成に必要なのは、このタイプのランタイム情報です。

例:

ClassManifest[T]を暗黙的なパラメーターとしてメソッドに渡すことにより、この情報を提供する必要があります。

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 
} 

省略形として、代わりに型パラメーターTでコンテキストbound1を使用できます。

(これを参照してください 説明のために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 
} 

Int、またはString、またはList[T]などのタイプでtabulateを呼び出す場合、Scalaコンパイラーは、暗黙的な引数としてtabulateに渡すクラスマニフェストを作成できます。 。

25
VonC

マニフェストは、JVM(ジェネリックをサポートしない)上で実行するために型消去されるジェネリック型を具体化することを目的としていました。しかし、彼らにはいくつかの深刻な問題がありました。それらは単純すぎて、Scalaの型システムを完全にサポートすることができませんでした。したがって、これらはScala 2.10でdeprecatedであり、TypeTags(本質的にはScalaコンパイラーに置き換えられます)それ自体は型を表すために使用されるため、Scala型を完全にサポートします)。違いの詳細については、以下を参照してください。

言い換えると

いつ必要ですか?

2013-01-04より前、 Scala 2.10がリリースされたとき

23

manifest in 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"
  }
}

manifestfunctionは、サンプルコードで提供したm: Manifest[T]を満たす暗黙のtype parameterを検索することがわかります。これはmanifest[String]でした。そのため、次のようなものを呼び出す場合:

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

関数で定義した現在のimplicit mmanifest[String]型であり、manifestmanifest[T]型の関数である場合、特定のmanifest[String]を検索し、そのような暗黙的なものがあるかどうかを確認します。

1
Tomer Ben David