【问题标题】:Can implicits be used to disambiguate overloaded definition?可以使用隐式来消除重载定义的歧义吗?
【发布时间】:2019-09-23 19:13:31
【问题描述】:

考虑以下方法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]implicit 不可用的事实来消除这两个重载的歧义?

【问题讨论】:

  • 目前无法测试,但您是否尝试过添加 Dummy implicit? - 其他选择是创建自己的类型类,泛化两者。
  • 我建议为此使用 typeclass - 这是在这种情况下经过验证的工作方法。
  • @LuisMiguelMejíaSuárez Dummy 似乎不起作用。

标签: scala implicit overload-resolution


【解决方案1】:

Scala 根据specification 仅考虑重载 分辨率的first 参数列表。两种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   

【讨论】:

    【解决方案2】:

    这是我在其他人建议的类型类解决方案中的尝试

    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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-07-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-10-18
      • 2011-12-05
      相关资源
      最近更新 更多