Scala:在impilicit参数

时间:2017-04-13 12:40:00

标签: scala type-alias

假设我有一个班级:

abstract class NumericCombine[A:Numeric,B:Numeric]{
        type AB <: AnyVal
    }

我想定义一个返回类型NumericCombine[A,B].AB的值的函数。例如:

def plus[A: Numeric,B:Numeric](x: A, y: B): NumericCombine[A,B].AB

但编译器不允许我在plus中引用.AB

仅供参考,this是这个问题的背景。

我想提供:

implicit object IntFloat extends NumericCombine[Int,Float]{override type AB = Float}
implicit object FloatInt extends NumericCombine[Float,Int]{override type AB = Float}

及其他44位朋友(7 * 6-2),以便我可以定义plus,如下所示:

def plus[A: Numeric,B:Numeric](x: A, y: B): NumericCombine[A,B].AB =
{
type AB = Numeric[NumericCombine[A,B].AB]
implicitly[AB].plus(x.asInstanceOf[AB],y.asInstanceOf[AB])
}

plus(1f,2)//=3f
plus(1,2f)//=3f

我知道Scala中的值转换允许我定义

def plus[T](a: T, b: T)(implicit ev:Numeric[T]): T = ev.plus(a,b)

并按照建议here实现上述行为,但由于我想将此函数用作更大函数的一部分(在本文提到的链接中描述),我需要参数化AB的函数。

更新

我在这方面取得了一些进展。

我的NumericCombine现在看起来像这样:

abstract class NumericCombine[A: Numeric, B: Numeric] {
        type AB <: AnyVal

        def fromA(x: A): AB
        def fromB(y: B): AB

        val numeric: Numeric[AB]

        def plus(x: A, y: B): AB = numeric.plus(fromA(x), fromB(y))
        def minus(x: A, y: B): AB = numeric.minus(fromA(x), fromB(y))
        def times(x: A, y: B): AB = numeric.times(fromA(x), fromB(y))
    }

和我的加号功能如下:

def plus[A: Numeric, B: Numeric](x: A, y: B)(implicit ev:NumericCombine[A,B])
        : ev.AB = ev.plus(x, y)

需要plus的加权平均函数最终变得有点复杂:

def accumulateWeightedValue[A: Numeric,B: Numeric]
            (accum: (A, NumericCombine[A, B]#AB), ValueWithWeight: (A, B))
            (implicit combine: NumericCombine[A, B], timesNumeric: Numeric[NumericCombine[A, B]#AB])
            :(A,NumericCombine[A, B]#AB)=

这是一个需要(A,AB),(A,B)并返回(A,AB)的函数。我在weightedSum内部使用它,它只聚合在这个上面:

def weightedSum[A: Numeric,B: Numeric](weightedValues: GenTraversable[(A, B)])
(implicit numericCombine: NumericCombine[A, B], plusNumeric: Numeric[NumericCombine[A, B]#AB])
: (A, NumericCombine[A, B]#AB)

现在,编译好了。它似乎与第二个隐式参数有问题。即,当我使用隐含的值NumericCombine[Int,Float]来运行时,使用数字[AB]。它给了我:

  

找不到参数plusNumeric的隐含值:   数字[NumericCombine [整数,浮点] #AB]

请注意,在NumericCombine中,我有一个Numeric [AB],它可用于隐式查找。在[Int,Float]

的情况下,将其存储在本地
val lst: Seq[(Int, Float)] =List((1,3f),(1,4f))
implicit val num: Numeric[Float] = IntFloat.numeric //IntFloat extends NumericCombine[Int,Float]
weightedSum(lst)
在调用需要它的函数之前,在局部变量中

似乎没有任何影响。那么为什么它会被隐式系统所吸引。

3 个答案:

答案 0 :(得分:3)

只需使用

def plus[A: Numeric,B:Numeric](x: A, y: B): NumericCombine[A,B]#AB

请注意#(哈希)而不是.(点)。这被称为&#34;类型投影&#34;。点符号称为&#34;路径依赖类型&#34;。我告诉你这些名字,以便您可以轻松地谷歌获取更多信息。简单地说,#用于从类/特征访问类型,和。用于从对象/值访问类型。

示例:

trait Foo {
    type T   
}

val fooObj: Foo = new Foo {
    type T = Int
}

type t1 = fooObj.T
type t2 = Foo#T

答案 1 :(得分:1)

@ slouc的答案替代

.main-nav__sub-menu-item-link

我还提升<nav class="main-nav"> <div class="main-nav__logo"> <a class="main-nav__logo-link">Company Name</a> </div> <ul class="main-nav__menu"> <li class="main-nav__menu-item"> <a class="main-nav__menu-item-link">Products</a> <ul class="main-nav__sub-menu"> <li class="main-nav__sub-menu-item"> <a class="main-nav__sub-menu-item-link">Foo</a> </li> <li class="main-nav__sub-menu-item"> <a class="main-nav__sub-menu-item-link">Bar</a> </li> <li class="main-nav__sub-menu-item"> <a class="main-nav__sub-menu-item-link">Baz</a> </li> </ul> </li> <li class="main-nav__menu-item"> <a class="main-nav__menu-item-link">Services</a> </li> <li class="main-nav__menu-item"> <a class="main-nav__menu-item-link">About Us</a> </li> </ul> </nav>

def plus[A, B](x: A, y: B)(implicit ev: NumericCombine[A, B]): ev.AB

这将允许实际实现NumericCombine,不需要强制转换:

trait NumericCombine[A, B] {
  type AB <: AnyVal
  def fromA(a: A): AB
  def fromB(b: B): AB
  val num: Numeric[AB]
}

abstract class NumericCombineImpl[A, B, R](implicit val num: Numeric[R], f1: A => R, f2: B => R) {
  type AB = R
  def fromA(a: A) = f1(a)
  def fromB(b: B) = f2(b)
}

implicit object IntFloat extends NumericCombineImpl[Int,Float,Float]
...

答案 2 :(得分:1)

* 2017年4月18日:根据作者*

的最新代码进行更新
  

* 2017年4月19日*

     
      
  • 添加NumericCombine#Implicits以获得谨慎
  •   
  • 删除AnyVal个约束以支持所有Numeric类型,例如BigInt
  •   
  • 重构NumericCombine
  •   

您需要Aux pattern

import scala.collection.GenSeq

trait NumericCombine[A, B] {
  type AB

  def fromA(x: A): AB

  def fromB(y: B): AB

  val numericA: Numeric[A]
  val numericB: Numeric[B]
  val numericAB: Numeric[AB]

  // For convenience, caller can 'import combine.Implicits._'
  // to bring the Numeric's into the current scope
  object Implicits {
    implicit def implicitNumericA = numericA

    implicit def implicitNumericB = numericB

    implicit def implicitNumericAB = numericAB
  }

  def plus(x: A, y: B): AB = numericAB.plus(fromA(x), fromB(y))

  def minus(x: A, y: B): AB = numericAB.minus(fromA(x), fromB(y))

  def times(x: A, y: B): AB = numericAB.times(fromA(x), fromB(y))
}

object NumericCombine {
  type Aux[A, B, _AB] = NumericCombine[A, B] {
    type AB = _AB
  }

  private def combine[A, B, _AB](fa: A => _AB, fb: B => _AB)
                               (implicit
                                _numericA: Numeric[A],
                                _numericB: Numeric[B],
                                _numericAB: Numeric[_AB]
                               ): NumericCombine[A, B] = new NumericCombine[A, B] {
      override type AB = _AB

      override def fromA(x: A): AB = fa(x)

      override def fromB(y: B): AB = fb(y)

      override val numericA: Numeric[A] = _numericA
      override val numericB: Numeric[B] = _numericB
      override val numericAB: Numeric[AB] = _numericAB
    }

  implicit lazy val IntFloat  = combine[Int, Float, Float](_.toFloat, identity)
  implicit lazy val BigIntBigDecimal  = combine[BigInt, BigDecimal, BigDecimal](i => BigDecimal(i), identity)

}

implicit class ValuesWithWeight[A, B](val weightedValue: (A, B)) {
  def weight: A = weightedValue._1

  def value: B = weightedValue._2
}

def weightedSum[A, B, AB]
(valuesWithWeight: GenSeq[(A, B)])
(implicit combine: NumericCombine.Aux[A, B, AB]):
(A, AB) = {

  import combine.Implicits._

  val z: (A, AB) =
    (combine.numericA.zero, combine.numericAB.zero)

  def accumulateWeightedValue(accum: (A, AB), valueWithWeight: (A, B)): (A, AB) = {
    val weightedValue = combine.times(valueWithWeight.weight, valueWithWeight.value)
    (
      combine.numericA.plus(accum.weight, valueWithWeight.weight),
      combine.numericAB.plus(accum.value, weightedValue)
    )
  }

  valuesWithWeight.aggregate(z)(
    accumulateWeightedValue,
    // dataOps.tuple2.plus[A,AB]
    {
      case ((a1, ab1), (a2, ab2)) =>
        (combine.numericA.plus(a1, a2) ->
          combine.numericAB.plus(ab1, ab2))
    }
  )
}

weightedSum(Seq(1 -> 1.5f, 2 -> 1f, 3 -> 1.7f))
weightedSum(Seq(BigInt(1) -> BigDecimal("1.5"), BigInt(2) -> BigDecimal("1"), BigInt(3) -> BigDecimal("1.7")))