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为双精度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在您的代码中)是一些A形成 a Ring(任何人Numeric A都会)的值。所以你的第一个例子有效。但是在第二个示例中没有这样的隐式(noLiteralDoubleMultiplicativeSemigroupOps或类似),因此第二个示例无法编译。

于 2015-06-03T13:44:05.637 回答
2

要扩展来自 lmm 的答案:

要将 T 类型的某物与整数相加或相乘,spire 只需要spire.algebra.Ring[T]类型类,该类型类是spire.algebra.Numeric[T]扩展的。

参见例如 spire.syntax.Ops 中的LiteralIntAdditiveSemigroupOps

要将 T 类型的某物添加或相乘到双精度数,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 的一个元素并使用 T 的+操作将其求和 n 次。

例如,将 Double 添加到 T 的结果也必须是 T。但是为此,您需要一种将Double转换为 T 的方法,这要复杂得多,并且仅在spire.algebra.Field[T]中可用( fromDouble 方法)。看看 Field 中的泛型 fromDouble 方法。这是一些非常复杂的代码,它利用了 Field[T] 的div操作,而 Ring[T] 上当然不存在这种操作。

如何修改你的代码

如果您出于某种原因真的想乘以双精度,则必须像这样修改方法:

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