问题
Consider the following overloaded definition of method 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
}
I would like second definition which returns Double
only to be used in the case of Integral
types, however
mean(List(1,2,3,4))
results in compiler error
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))
^
Is there any way to use the fact that Fractional[Int]
implicit is not available in order to disambiguate the two overloads?
回答1:
Scala only considers the first argument list for the overload resolution, according to the specification. Both mean
methods are deemed equally specific and ambiguous.
But for implicit resolution the implicits in scope are also considered, so a workaround could be to use a magnet pattern or a type class. Here is an example using the magnet pattern, which I believe is simpler:
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
}
}
}
With this definition it works normally:
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
回答2:
Here is my attempt at typeclass solution as suggested by others
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)))
}
which outputs
2.5
2.5
2.5
来源:https://stackoverflow.com/questions/58068778/can-implicits-be-used-to-disambiguate-overloaded-definition