Scala 2.9での不思議なキャスト(.asInstanceOf [T])動作

私は並列計算のためのソフトウェアを書いています。ソフトウェアは完全に汎用的なので、シーケンスをラップするクラスが必要です。

今、私はマップ機能で奇妙な動作を経験しています。マップ関数は入力として関数オブジェクトを取り、入力引数とは異なる型を出力することができます。

次のコードを見てください:

class SeqMPJ[T](indexedSeq: IndexedSeq[T]) {

  val seq = indexedSeq.view //Lazy
  val freeRanks = MPJEnv.getFreeRanks(seq.size)
  //Get n free ranks
  val commGroup = MPI.COMM_WORLD.group.Incl(freeRanks.toArray)
  //Communicator for this sequence
  val comm = MPI.COMM_WORLD.Create(commGroup)

  val opIndex = globalRank % seq.size
  var operand: Any = seq(opIndex)

  if (!isOnline)
    error("Cannot use MPJ-abstractions outside parallelize body...")

  //Only works for p=n now
  def mapMPJ[U](f: (T) => U): SeqMPJView[U] = {
    if (freeRanks.contains(globalRank)) { //Process is part of this operation
      operand = f(operand.asInstanceOf[T])
    }
    return new SeqMPJView(operand.asInstanceOf[U], comm, freeRanks)
  }

...

Notice that in the function mapMPJ[U](f: (T) => U):SeqMPJView[U] , the function f has input type T and output type U. This means, after applying f to the variable "operand", operand is of type U, however, this happens inside the if-block. In other words, depending on the state, operand has either type U or T. Now, when I cast to U, it always succeeds. Even when the condition in the if-block fails. As I see it, the program should fail when casting operand.asInstanceOf[U] if the program does not enter the if-block.

使用例はこの行列乗算にあります:

val M = 2
val N = 2

val A = Array(
  Array(1.0, 2.0),
  Array(3.0, 4.0))

val B = Array(
  Array(1.0, 2.0),
  Array(3.0, 4.0))

val Bt = B.transpose

/*
 * DNS using underlying MPI
 */
parallelize(args) {

  for (i <- 0 until M; j <- 0 until N)
    A(i) zip Bt(j) mapMPJ { case (a, b) => a * b } reduceMPJ (_ + _)

  if (globalRank == 0)
    println("CHECK RESULT:\n" + Matrix(A) * Matrix(B))

}

このプログラムは最新のeclipse scala IDEを使用して完全にコンパイルして実行します。私は他のコンパイラを試していませんが、おそらく私は盲目ですが、とても時間を費やしましたので、私はいくつかの助けを祈っています:)

編集

ArrayからseqMPJ、FYIへの暗黙的な変換があります。

0
あなたが型チェッカーを騙っているようなやり方は邪魔になり、おそらく不必要です。あなたはオペランドの型として[A、B]のどちらかを使うと考えましたか?コメントは、他の状態ではオペランドを必要としないことを示唆しています。 ifが取られていない場合、後でオペランドに関数を適用しますか?その場合、私は使用したいと思います: trait Result [U] {def res:U} case class Computed [U](res:U)extends Result [U] caseクラスToCompute [T、U](t: T、f:T => U)は、結果[U] {def get = f(t)} を拡張し、 operand [U] (名前を変更してください)。
追加された 著者 Blaisorblade,
どちらかは標準のScalaデータ型です。おそらく、 SeqMPJView を定義する際に Option を使用して、値を渡す必要がないことを指定する必要があります。 A 型または B 型のいずれかの値を指定することができます( B 私は組合です。
追加された 著者 Blaisorblade,
if節が失敗した場合にオペランドを必要としない場合があります。しかし、私は新しいSeqMPJViewを返さなければなりません! [A、B]のどちらか?いずれかのキーワードですか?
追加された 著者 Felix,

1 答え

キャストは、ジェネリック型の引数について、あなたが何をしているのか、実際の型が何であるかをコンパイラにアサーションするだけです。それらはすべて実際には AnyRef == java.lang.Object (またはバウンディングタイプが何であれ)です。あなたがそれに嘘をついていると、(タイプが間違ったタイプが使用されているために実行時の例外が発生するまで)信じられます。あなたが正しいタイプを持っているかどうかを知りたければ、マニフェストでチェックする必要があります。

ここに例があります:

def example[A: ClassManifest,B: ClassManifest](a: A) = {
  if (implicitly[ClassManifest[A]] <:< implicitly[ClassManifest[B]]) a.asInstanceOf[B]
  else throw new Exception("Wrong!")
}

あなたが動作しないキャストをしようとすると、これはあなたに叫ぶでしょう:

scala> example[List[Int],List[String]](List())
java.lang.Exception: Wrong!

コードを適宜変更することができます。

implicitly[ClassManifest[U]].erasure.isAssignableFrom(operand.getClass)
4
追加された
@Felix - <:<は "のサブクラス"を意味し、暗黙的に[ClassManifest [A]] は " A 」を選択します。 Blaisorbladeはもう一方の呼び出しについて説明したので、タイプがあなたが求めているもの(またはそのサブタイプ)であることを確認するランタイムチェックであることが分かります。
追加された 著者 Rex Kerr,
暗黙的に[ClassManifest [U]]。erasure.isAssignableFrom(operan&zwnj; d.getClass)は、型互換性のランタイムテストです。型Uの変数にオペランドを代入できる場合はtrueを返します。オペランドはUのサブタイプです( docs.oracle.com/javase/6/docs/api/java/lang/…
追加された 著者 Blaisorblade,
「暗黙のうちに[ClassManifest [A]] <:暗黙的に[ClassManifest [B]] 'そして、私は暗黙のうちに[ClassManifest [U]]という目的を理解していません。erasure.isAssignableFrom operand&zwnj; d.getClass)
追加された 著者 Felix,
なぜ私のコードがコンパイルされるのかを説明するので、私は答えを受け入れました。
追加された 著者 Felix,