2017-04-13 86 views
2

抽象了路径依赖型比方说,我有一个类:斯卡拉:在impilicit参数

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 

但编译器不让我参考.AB加号。

FYI,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 

我知道的事实上,在斯卡拉值转换允许我定义

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

并实现上述行为作为建议但由于我想将此函数用作较大函数的一部分(在作为此问题的上下文中所述的链接中进行了描述),因此我需要使用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) 

现在,这个编译罚款。它似乎对第二个隐式参数有问题。即数字[AB]当我运行与隐含的价值为NumericCombine[Int,Float]目前。它给我:

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

注意,在NumericCombine,我有一个数字[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) 
在一个局部变量

调用函数需要它之前似乎并没有产生任何影响。那么为什么它会被隐式系统拾取。

+1

的'NumericCombine [A,B]#AB'觉得作为'NumericCombine的所有实例的常见类型'AB' [A,B]'可以存在。所以'accum'的类型是错误的。 –

+0

我明白了。那么有没有什么方法可以抽象出这些AB?允许其他函数使用这个通用加号? – ShS

+0

你说'accumulateWeightedValue'被用在'weightedSum'里面。所以,只要将它设为本地方法,就可以使用'numericCombine.AB'。 “A”和“B”不应该需要'加数字'或'数字'约束。 –

回答

1

* 2017年4月18日:从笔者最新的代码更新的基础*

* 2017年4月19日*

  • 为convinence添加NumericCombine#Implicits
  • 删除AnyVal约束来支持任何类型的例子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"))) 
+0

有趣!所以根据我的理解,你建议:1)用“AB”类型参数参数化“私有”函数,并根据“Aux”模式构造正确的“数字[A,B]”。我仍然不确定如何在只使用“A”和“B”的“public”函数内使用一个使用'Aux'参数的函数。即我想在'weightedSum'中使用'accumulateWeightedValue'。通过'accumulateWeightedValue [A,B,combine.AB]'汇总给我:**依赖类型的scala方法不能转换为函数值** – ShS

+0

这里是我的代码:https://pastebin.com/4kywcrpe – ShS

+0

嗨@ShS,关键是你需要为类型推断声明AB类型的参数,然后隐式查找才能正常工作。您可以访问'辅助模式'链接了解更多详情。 – PH88

3

只需使用

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

注意#(散),而不是.(点)。这被称为“类型投影”。点符号被称为“路径依赖类型”。我告诉你这些名字,以便你可以轻松获得更多信息。简单地说,#用于访问类/特征的类型,以及。用于访问对象/值的类型。

例子:

trait Foo { 
    type T 
} 

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

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

不幸的是,我不认为你可以为这个特定的问题“使用”它:'plus [Int,Float]'的返回类型是'NumericCombine [Int,Float]#AB'而不是'Float'。 –

+0

这让我感到困惑。我已经定义了def'plusAB [A:Numeric,B:Numeric](x:A,y:B)(隐式ev:NumericCombine [A,B]):ev.AB = ev.plus(x,y)'这完美地工作。现在,如果我使用完全相同的签名创建另一个函数,但重定向到此函数,即:'plusAB2 [..](...)= plusAB(x,y)'我得到:** NumericCombine类型的表达式[ A,B] #AB不符合预期的类型:ev.AB **我是否期待这项工作很愚蠢? – ShS

+0

嗯实际上阅读你答案的文字开始给我一个问题是什么的概念。我现在已经用一些进展和一个问题更新了我的问题,这是我认为我上面描述的另一个表现。谷歌搜索“斯卡拉隐式类型投影”带来了一些我不太明白的东西。 – ShS

1

到@ slouc的回答另一种方法是

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] 
... 

这将使实际执行plus,无需要强制转换:

def plus[A, B](x: A, y: B)(implicit ev: NumericCombine[A, B]): ev.AB = 
    ev.num.plus(ev.fromA(x), ev.fromB(y)) 
+0

公顷我实际上正在研究这个确切的事情,因为原来的计划变得单调乏味。除此之外,我决定在NumericCombine中移动加号功能等。它只是更少的类和更少的隐含参数。 – ShS

+0

这就是我最终实现的加号。 https://pastebin.com/ne0KwfnK我很乐意收到您的反馈。我有点不确定要R是AnyVal的子类型。 – ShS

+0

我已更新我的问题,并取得了一些进展。 – ShS