6

データ構造を実装しています。Scalaの標準的なコレクション特性のいずれにも直接混在することはありませんがto[Col[_]]、ビルダーファクトリがあれば、標準的なScalaコレクションを生成できるメソッドを含めたいと思います。

ここで、これを想定します。コピー元GenTraversableOnce

trait Foo[+A] {
  def to[Col[_]](implicit cbf: CanBuildFrom[Nothing, A, Col[A]]): Col[A]
}

これはで失敗しerror: covariant type A occurs in invariant positionます。

では、どうすればGenTraversableOnceこれを達成できますか?ソースコードを見ると、annotation.unchecked.uncheckedVariance...が追加されていることがわかります。

それは汚いトリックのように見えます。タイパーがこれを正常に拒否した場合、これを安全に切り替えてオフにするにはどうすればよいuncheckedVarianceですか?

4

3 に答える 3

2

これは、@uncheckedVarianceアノテーションを使用して型システムを迂回し、差異チェックを無視しているためです。

単純import scala.annotation.unchecked.uncheckedVarianceに、差異チェックを無効にするタイプに注釈を付けます。

def to[Col[_]](implicit cbf: CanBuildFrom[Nothing, A, Col[A @uncheckedVariance]]): Col[A @uncheckedVariance]

関連する回答のより完全な説明を参照してください。

于 2013-03-08T18:45:35.953 に答える
2

@axel22が言及した他の質問へのリンクを読みました。ただし、それでも実際の理由ではないようです(GenTraversableOnceバリアントコレクションと不変コレクションの両方で機能できるようになります。共変ですA)。

たとえば、以下はタイパーを強制せずに正しく機能します。

import collection.generic.CanBuildFrom

trait Foo[+A] {
  def to[A1 >: A, Col[_]](implicit cbf: CanBuildFrom[Nothing, A1, Col[A1]]): Col[A1]
}

case class Bar[A](elem: A) extends Foo[A] {
  def to[A1 >: A, Col[_]](implicit cbf: CanBuildFrom[Nothing, A1, Col[A1]]): Col[A1]= {
    val b = cbf()
    b += elem
    b.result()
  }
}

私の意見では、これが正しい署名になります。しかし、もちろん、それは醜くなります:

val b = Bar(33)
b.to[Int, Vector]

したがって、の使用についての私の解釈は、署名@uncheckedVarianceで要素タイプを(上限として)繰り返す必要がないようにすることだけです。to

しかし、分散を無視することで実行時エラーが発生するケースを想像できるとしたら、それでも答えは得られません。

于 2013-03-08T18:56:51.743 に答える
2

差異チェックはタイプチェックの非常に重要な部分であり、スキップするとランタイムタイプエラーが発生しやすくなります。ここでは、無効なランタイム値が入力されたタイプを出力して示すことができます。ただし、型キャスト例外でクラッシュさせることはまだできていません。

import collection.generic.CanBuildFrom
import collection.mutable.Builder
import scala.annotation.unchecked.uncheckedVariance

trait Foo[+A] {
  def toCol[Col[_]](implicit cbf: CanBuildFrom[Nothing, A, Col[A @uncheckedVariance]]): Col[A @uncheckedVariance]
}

object NoStrings extends Foo[String] {
  override def toCol[Col[_]](implicit cbf: CanBuildFrom[Nothing, String, Col[String]]): Col[String] = {
    val res : Col[String] = cbf().result
    println("Printing a Col[String]: ")
    println(res)
    res
  }
}

case class ExactlyOne[T](t : T)

implicit def buildExactlyOne = new CanBuildFrom[Nothing, Any, ExactlyOne[Any]] {
  def apply() = new Builder[Any, ExactlyOne[Any]]  { 
    def result = ExactlyOne({}) 
    def clear = {}
    def +=(x : Any) = this
  }
  def apply(n : Nothing) = n
}

val noStrings : Foo[Any] = NoStrings
noStrings.toCol[ExactlyOne]

ここprintln(res)res : Col[String]プリントがありExactlyOne(())ます。ただし、ExactlyOne(())タイプがないためCol[String]、タイプエラーが発生します。

分散ルールを尊重しながら問題を解決するには、不変コードを特性から移動し、共変部分のみを保持し、暗黙の変換を使用して共変特性から不変ヘルパークラスに変換します。

import collection.generic.CanBuildFrom

trait Foo[+A] {
  def to[R](implicit cbf: CanBuildFrom[Nothing, A, R]): R
}

class EnrichedFoo[A](foo : Foo[A]) {
  def toCol[Col[_]](implicit cbf: CanBuildFrom[Nothing, A, Col[A]]): Col[A] = 
    foo.to[Col[A]]
}

implicit def enrich[A](foo : Foo[A]) = new EnrichedFoo(foo)

case class Bar[A](elem: A) extends Foo[A] {
  def to[R](implicit cbf: CanBuildFrom[Nothing, A, R]): R = {
    val b = cbf()
    b += elem
    b.result()
  }
}

val bar1 = Bar(3)
println(bar1.toCol[Vector])
于 2013-03-08T20:29:05.887 に答える