隐式可以用来消除重载定义的歧义吗?



考虑以下方法mean的重载定义:

def mean[T](data: Iterable[T])(implicit number: Fractional[T]): T = {
import number._
val sum = data.foldLeft(zero)(plus)
div(sum, fromInt(data.size))
}
def mean[T](data: Iterable[T])(implicit number: Integral[T]): Double = {
import number._
val sum = data.foldLeft(zero)(plus)
sum.toDouble / data.size
}

我想要第二个定义,它返回Double仅用于Integral类型的情况下,但是

mean(List(1,2,3,4))

导致编译器错误

Error: ambiguous reference to overloaded definition,
both method mean in class A$A16 of type [T](data: Iterable[T])(implicit number: Integral[T])Double
and  method mean in class A$A16 of type [T](data: Iterable[T])(implicit number: Fractional[T])T
match argument types (List[Int])
mean(List(1,2,3,4))
^

有没有办法利用Fractional[Int]隐式不可用的事实来消除两个重载的歧义?

根据规范,Scala 只考虑重分辨率的第一个参数列表。这两种mean方法被认为同样具体和模棱两可。

但对于隐式解析,还会考虑作用域中的隐式解析,因此解决方法可能是使用磁体模式或类型类。下面是一个使用磁铁图案的示例,我认为它更简单:

def mean[T](data: MeanMagnet[T]): data.Out = data.mean
sealed trait MeanMagnet[T] {
type Out
def mean: Out
}
object MeanMagnet {
import language.implicitConversions
type Aux[T, O] = MeanMagnet[T] { type Out = O }
implicit def fromFractional[T](
data: Iterable[T]
)(
implicit number: Fractional[T]
): MeanMagnet.Aux[T, T] = new MeanMagnet[T] {
override type Out = T
override def mean: Out = {
import number._
val sum = data.foldLeft(zero)(plus)
div(sum, fromInt(data.size))
}
}
implicit def fromIntegral[T](
data: Iterable[T]
)(
implicit number: Integral[T]
): MeanMagnet.Aux[T, Double] = new MeanMagnet[T] {
override type Out = Double
override def mean: Out = {
import number._
val sum = data.foldLeft(zero)(plus)
sum.toDouble / data.size
}
}
}

有了这个定义,它就可以正常工作:

scala> mean(List(1,2,3,4))
res0: Double = 2.5
scala> mean(List(1.0, 2.0, 3.0, 4.0))
res1: Double = 2.5
scala> mean(List(1.0f, 2.0f, 3.0f, 4.0f))
res2: Float = 2.5   

这是我尝试其他人建议的typeclass解决方案

trait Mean[In, Out] {
def apply(xs: Iterable[In]): Out
}
object Mean {
def mean[In, Out](xs: Iterable[In])(implicit ev: Mean[In, Out]): Out = ev(xs)
private def meanFractional[T](data: Iterable[T])(implicit number: Fractional[T]): T = {
import number._
val sum = data.foldLeft(zero)(plus)
div(sum, fromInt(data.size))
}
private def meanIntegral[T](data: Iterable[T])(implicit number: Integral[T]): Double = {
import number._
val sum = data.foldLeft(zero)(plus)
sum.toDouble / data.size
}
implicit val meanBigInt: Mean[BigInt, Double] = meanIntegral _
implicit val meanInt: Mean[Int, Double] = meanIntegral _
implicit val meanShort: Mean[Short, Double] = meanIntegral _
implicit val meanByte: Mean[Byte, Double] = meanIntegral _
implicit val meanChar: Mean[Char, Double] = meanIntegral _
implicit val meanLong: Mean[Long, Double] = meanIntegral _
implicit val meanFloat: Mean[Float, Float] = meanFractional _
implicit val meanDouble: Mean[Double, Double] = meanFractional _
import scala.math.BigDecimal
implicit val meanBigDecimal: Mean[BigDecimal, BigDecimal] = meanFractional _
}
object MeanTypeclassExample extends App {
import Mean._
println(mean(List(1,2,3,4)))
println(mean(List(1d,2d,3d,4d)))
println(mean(List(1f,2f,3f,4f)))
}

哪些输出

2.5
2.5
2.5

最新更新