2017-04-13 163 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