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


132

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

ブログの投稿Manifests:Reified Types by Jorge Ortizがそのいくつかを説明していますが、コンテキスト境界と一緒に使用する方法については説明していません。

また、何がClassManifest違うのManifestですか?

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


2
Manifest / ClassManifestの違いに関するメーリングリストでの議論がありました。scala
Arjan Blokzijl

回答:


197

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

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

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

マニフェストの一般的な用途の1つは、コレクションの静的な型に基づいてコードの動作を変えることです。たとえば、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

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

バインドコンテキストは、ほとんどのScalaでは型クラスを使用するのに適し、よくDebasishゴーシュで、ここで説明されているようだ。 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

ない完全な答えが、違いについてManifestClassManifest、あなたが例を見つけることができ、スカラ2.8 Arrayペーパー

残っている唯一の問題は、ジェネリック配列の作成を実装する方法です。Javaとは異なり、Scalaでは、が型パラメーターArray[T]であるインスタンスを新しく作成でき Tます。Javaに均一な配列表現が存在しないという事実を踏まえて、これをどのように実装できますか?

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

また、必ずしもすべての引数の型を知っていなくても、型の最上位クラスだけを知っていることから構築できるという名前の弱い形式も存在し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をtypeパラメーターで使用できます。

(例については、この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のコンパイラは、TABULATEへの暗黙の引数として渡すクラスマニフェストを作成することができます。


25

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

言い換えると

いつそれが必要ですか?

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


Scalaリフレクションは2.10でもまだ実験的であるため、まだ非推奨ではありません(ただし非推奨になります)。
Keros 2013

2013-01-04より前、またはそれに依存するAPIを使用している場合。
David Moles

1

ソース()もチェックアウトmanifestしてみましょう。scalaManifest.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 parametermanifest[String]

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

現在の場合はチェックしているimplicit mあなたは、あなたの関数で定義されているタイプのものでありmanifest[String]かつとしてmanifestのタイプの関数でありmanifest[T]、それは、特定の検索だろうmanifest[String]と、このような暗黙のがある場合、それは見つけるだろう。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.