3

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?

Mario Galic
  • 47,285
  • 6
  • 56
  • 98
  • 2
    Can not test right now, but have you tried adding the **Dummy** _implicit_? - Other option would be to create your own typeclass that generalizes over both. – Luis Miguel Mejía Suárez Sep 23 '19 at 19:33
  • Possible duplicate of [scala - Can I overload curried methods?](https://stackoverflow.com/questions/9268876/scala-can-i-overload-curried-methods) – Suma Sep 23 '19 at 20:02
  • 2
    I suggest using a typeclass for this - this is proven working method for such situations. – Suma Sep 23 '19 at 20:03
  • 1
    @LuisMiguelMejíaSuárez [`Dummy`](https://stackoverflow.com/questions/34745066/dummyimplicits-is-this-used-and-how) did not seem to work. – Mario Galic Sep 23 '19 at 20:57

2 Answers2

2

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   
Mario Galic
  • 47,285
  • 6
  • 56
  • 98
Kolmar
  • 14,086
  • 1
  • 22
  • 25
1

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
Mario Galic
  • 47,285
  • 6
  • 56
  • 98