0

次のポリモーフィック メソッドがコンパイルされます。

import spire.math._
import spire.implicits._

scala> def foo[A:Numeric](d : A) = 2 * d
foo: [A](d: A)(implicit evidence$1: spire.math.Numeric[A])A

scala> def foo[A:Numeric](d : A) = 2 + d
foo: [A](d: A)(implicit evidence$1: spire.math.Numeric[A])A

ただし、整数2を doubleに変更すると2.0、コンパイラは暗黙的な値が見つからないことについて不平を言います

scala> def foo[A:Numeric](d : A) = 2.0 + d
<console>:19: error: could not find implicit value for parameter ev: spire.algebra.Field[A]
       def foo[A:Numeric](d : A) = 2.0 + d
                                       ^

scala> def foo[A:Numeric](d : A) = 2.0 * d
<console>:19: error: could not find implicit value for parameter ev: spire.algebra.Field[A]
       def foo[A:Numeric](d : A) = 2.0 * d
                                       ^

SOに関する他の質問と回答のいくつかを理解しようとしましたが、これを解決する方法については賢明ではありません。

4

2 に答える 2

2

暗黙で何が起こっているかを確認する最も明確な方法は、使用することですreify(IDE で同様の機能が提供される場合があります)。

scala> import scala.reflect.runtime.universe.reify
import scala.reflect.runtime.universe.reify

scala> reify { def foo[A:Numeric](d : A) = 2 * d }
res1: reflect.runtime.universe.Expr[Unit] =
Expr[Unit]({
  def foo[A](d: A)(implicit evidence$1: Numeric[A]) = implicits.literalIntMultiplicativeSemigroupOps(2).$times(d)(evidence$1);
  ()
})

尖塔のソースを見ると、次のように表示されます。

final class LiteralIntMultiplicativeSemigroupOps(val lhs: Int) extends AnyVal {
  def *[A](rhs:A)(implicit ev: Ring[A]): A = ev.times(ev.fromInt(lhs), rhs)
}

これは、*メソッドをInts で使用できるようにする暗黙のクラスです。ただし、右側の値 (dコード内) がAa を形成するものRing(いずれかNumeric Aになります) である場合に限ります。したがって、最初の例は機能します。しかし、2 番目の例にはそのような暗黙的な (no などLiteralDoubleMultiplicativeSemigroupOps) がないため、2 番目の例はコンパイルされません。

于 2015-06-03T13:44:05.637 に答える
2

lmm からの回答を拡張するには:

型 T の何かを整数に加算または乗算するには、spire はspire.algebra.Numeric[T]が拡張するspire.algebra.Ring[T]型クラスのみを必要とします。

たとえば、spire.syntax.Ops の LiteralIntAdditiveSemigroupOps を参照してください

タイプ T のものを double に加算または乗算するには、spire は spire.algebra.Field[T] 型クラスのインスタンスを必要としますが、それはspire.algebra.Numeric [ T]では拡張されませ

たとえば、spire.syntax.Ops の LiteralDoubleAdditiveSemigroupOps を参照してください

この設計上の決定の理由:

たとえば、Int を T に追加した結果は T でなければなりません。したがって、Int を T に変換する方法が必要です。これはspire.algebra.Ring[T] (fromInt メソッド) で利用できます。整数 n を T に変換するには、 T の1 つの要素を使用し、T の+演算を使用して n 回合計します。

たとえば、Double を T に追加した結果も T でなければなりません。しかし、そのためには、 Doubleを Tに変換する方法が必要です。これは、はるかに複雑で、spire.algebra.Field[T]でのみ使用できます( fromDouble メソッド)。Field のジェネリック fromDouble メソッドを見てみましょう。これは Field[T] のdiv操作を利用するかなり複雑なコードですが、もちろん Ring[T] には存在しません。

コードを変更する方法

なんらかの理由で double を乗算したい場合は、メソッドを次のように変更する必要があります。

def foo[A: Field](d: A) = 2.0 * d
于 2015-06-03T14:01:38.830 に答える