暗示混乱

时间:2015-06-03 13:14:33

标签: scala spire

以下多态方法编译:

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
                                       ^

我试图理解其他一些问题和答案,但我不知道如何解决这个问题。

2 个答案:

答案 0 :(得分: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)
}

这是一个隐式类,它使*方法在Int上可用,前提是右侧的值(代码中为d)为A形成Ring(任何Numeric A都会)。所以你的第一个例子有效。但是在第二个例子中没有这样的隐式(没有LiteralDoubleMultiplicativeSemigroupOps或类似的东西),所以第二个例子没有编译。

答案 1 :(得分:2)

扩展lmm的答案:

将T类型的东西添加或乘以整数,spire只需要spire.algebra.Ring[T]类型类,spire.algebra.Numeric[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的一个元素,并使用T的 + 操作将其求和n次。

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

如何修改代码

如果你真的想因为某种原因乘以双倍,你必须修改这样的方法:

def foo[A: Field](d: A) = 2.0 * d