2016-04-06 11 views
3

スカラ2.10を使用していて、ジェネリックパラメータの型推論を使用するときに私が奇妙な振る舞いをすることがわかりました。 (iに注意してくださいだけでダミー変数である)以下の例を考えてみましょう:スカラジェネリック型の推論がClassTagと矛盾する

scala> import scala.reflect.{ClassTag,classTag} 
    | 
    | def broken[T : ClassTag](i : Int) : List[T] = { 
    | println("ClassTag is : " + classTag[T]) 
    | List.empty[T] 
    | } 

import scala.reflect.{ClassTag, classTag} 

broken: [T](i: Int)(implicit evidence$1: scala.reflect.ClassTag[T])List[T] 

scala> broken(3) 
ClassTag is : Nothing 
res0: List[Nothing] = List() 

scala> val brokenVal : List[String] = broken(3) 
ClassTag is : Nothing 
brokenVal: List[String] = List() 

コールbroken(3)関数内print文が推測されClassTagと一致するには、一貫性のあると思われます。

ただし、2番目の文は、ClassTagのコンパイラが推論する内容と、関数内に出力される内容と実際の戻り値の種類との間に矛盾があります。

コンパイラがという型宣言からClassTagを推測することが予想されます。これは間違っていますか?誰かが私を啓発することはできますか?

ここではいくつかのコードでコレクションをフィルタリングするために実際にclasstagを使用しています(具体的にはと比較してTを明示的に指定しないと失敗します)。

答えて

1

返信のタイプは、List[Nothing]です。したがって、ClassTagは完全に一貫しています。これは、Listが共変(List[A]の代わりにList[+A]と定義されているため)です。この共分散はlist = NilNil.type = List[Nothing])のような文を書くことを可能にしますが、List[Nothing]はサブタイプがList[String]なので、コンパイラはリスト型引数としてボトム型を推論することもできます。したがって、実際の型の不一致は、戻り型と型の間のものです。brokenValこの制限を克服するために

、あなたはすなわち、あなたの戻り値の型として不変タイプを使用する場合があります:他の側では

import scala.reflect.ClassTag 

class Invariant[T] 

def f[T: ClassTag](i: Int): Invariant[T] = { 
    println("ClassTag: " + implicitly[ClassTag[T]]) 
    new Invariant[T] 
} 

val ret: Invariant[String] = f(3) 
// ClassTag: java.lang.String 
// ret: Invariant[String] = [email protected] 

を、あなたが渡すことによって、例えば、何か他のものからTを推測するために、コンパイラを聞かせてかもしれません引数としてタイプTの値。この例は、バニラListで動作させることはできません。そのバラツキを変更することはできません。

+0

私は実際に共分散であり、この問題にぶつかるrx Observablesを使用していました。私は 'collect'ステートメントでClassTagを使用して特定のタイプのアイテムのみを取り出しました。 'observable collect {case t:T => t}'という引数を渡す値はありません。あなたが言うように、推論の問題を修正しました。ご意見をいただきありがとうございます。 – Luciano

+0

このような状況でもタイプシグネチャを収集する可能性のあるマクロソリューションがありますが、ここでマクロを使用するのは難しいと思います。変数型を省略し、明示的に '[T]'を指定してください。 – Maxim