Can implicits be used to disambiguate overloaded definition?

。_饼干妹妹 提交于 2020-01-04 04:53:17

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!