最初に注意すべきことmath.max
は、オーバーロードされていることです。コンパイラが予想される引数の型に関するヒントを持っていない場合、オーバーロードの 1 つを選択するだけです (どのオーバーロードが選択されるかを制御する規則についてはまだ明確ではありませんが、次のようになります。この記事が終わる前にクリアしてください)。
どうやらInt
、他のパラメーターよりもパラメーターを取るオーバーロードが優先されます。これはreplで見ることができます:
scala> math.max _
res6: (Int, Int) => Int = <function2>
次の最初のメソッドは (数値の拡大変換によって) コンパイルされ、2 番目のメソッドはコンパイルされないため、このメソッドが最も具体的です。
scala> (math.max: (Float,Float)=>Float)(1,2)
res0: Float = 2.0
scala> (math.max: (Int,Int)=>Int)(1f,2f)
<console>:8: error: type mismatch;
found : Float(1.0)
required: Int
(math.max: (Int,Int)=>Int)(1f,2f)
^
テストは、ある関数が他のパラメーター型に適用されるかどうかであり、そのテストには変換が含まれます。
ここで問題は、なぜコンパイラは正しい期待される型を推測できないのかということです。の型Array(1f, 3f, 4f)
がArray[Float]
reduce
: に置き換えると手掛かりが得られ、reduceLeft
問題なくコンパイルされます。
したがって、これは と の署名の違いに関係していreduceLeft
ますreduce
。次のコード スニペットでエラーを再現できます。
case class MyCollection[A]() {
def reduce[B >: A](op: (B, B) => B): B = ???
def reduceLeft[B >: A](op: (B, A) => B): B = ???
}
MyCollection[Float]().reduce(max) // Fails to compile
MyCollection[Float]().reduceLeft(max) // Compiles fine
サインが微妙に違う。
2reduceLeft
番目の引数では (コレクションの型) が強制されるA
ため、型の推論は自明です: A==Float (コンパイラが認識する) の場合、コンパイラは、の唯一の有効なオーバーロードが2 番目の引数としてmax
a を取るものであることを認識します。 Float
. コンパイラは 1 つの ( ) しか見つけず、もう 1 つmax(Float,Float)
の制約 ( B >: A
) は自明に満たされます (B == A == Float
このオーバーロードに関して)。
の場合は異なりreduce
ます。最初の引数と 2 番目の引数の両方が、任意の (同じ) スーパータイプのA
(つまりFloat
、この特定のケースではの) である可能性があります。これははるかに緩い制約であり、この場合、コンパイラーは可能性が 1 つしかないことを認識できると主張できますが、コンパイラーはここで十分にスマートではありません。コンパイラがこのケース (つまり、これは推論のバグ) を処理できるはずなのかどうか、私にはわからないと言わざるを得ません。型推論は scala ではトリッキーなビジネスであり、私が知る限り、仕様は推論できるものとできないものについて意図的にあいまいです。
次のような便利なアプリケーションがあるため:
scala> Array(1f,2f,3f).reduce[Any](_.toString+","+_.toString)
res3: Any = 1.0,2.0,3.0
型パラメーターのすべての可能な置換に対してオーバーロードの解決を試みるのは高価であり、予想される型に応じて結果が変わる可能性があります。または、あいまいなエラーを発行する必要がありますか?
を使用すると、オーバーロードの解決が最初に行われる と、最初に param 型が解決されるバージョン-Xlog-implicits -Yinfer-debug
の違いが示されます。reduce(math.max)
scala> Array(1f,2f,3f).reduce(math.max(_,_))
[solve types] solving for A1 in ?A1
inferExprInstance {
tree scala.this.Predef.floatArrayOps(scala.Array.apply(1.0, 2.0, 3.0)).reduce[A1]
tree.tpe (op: (A1, A1) => A1)A1
tparams type A1
pt ?
targs Float
tvars =?Float
}