2011-03-03 190 views
2

假設您想要將一些方法添加到所有智能類型。這可以是這樣的:在Scala中隱式轉換泛型和非泛型子類型

import collection.generic.CanBuildFrom 

class Foo[P, S[X] <: Iterable[X]](val s : S[P]) { 
    def bar(j : P)(implicit bf : CanBuildFrom[S[P],P,S[P]]) : S[P] = { 
    val builder = bf(s) 
    builder ++= s 
    builder += j 
    builder.result 
    } 

    def oneBar(j : P)(implicit bf : CanBuildFrom[S[P],P,S[P]]) : P = bar(j).head 
} 

implicit def iter2foo[P, S[X] <: Iterable[X]](s : S[P]) = new Foo[P,S](s) 

現在,類似的代碼

println(Seq(1,2,3,4) bar 5) 

編譯和執行順利。然而,

println((1 to 4) bar 5) 

導致

error: value bar is not a member of scala.collection.immutable.Range.Inclusive 
with scala.collection.immutable.Range.ByOne 

我想這可能是因爲隱式轉換需求(?)是參數的類型有一個類型參數(Range還沒有)。但是

implicit def iter2foo[P, S <: Iterable[P]](s : S) = new Foo[P,Iterable](s) 

不會改變任何東西。請注意0​​延伸Iterable[Int]

我在做什麼錯?我怎樣才能寫出一個適用於所有Iterable的子類型的隱式轉換?

編輯:我只是注意到按預期(上REPL)更簡單

implicit def iter2foo[P](s : Iterable[P]) = new Foo[P,Iterable](s) 

作品。還是呢?這種解決方案有什麼缺點嗎?

編輯2:的缺點是的bar靜態結果類型只會Iterable[P],而不是更具體的類型。雖然構建的集合具有正確的(實際)類型。

回答

2

不幸的是,Range延伸Iterable[Int]並不重要,這確實是類型參數的問題。這是一深一過,甚至核心庫受到它的地方(只要看看評論中Manifest

你也會碰到它,如果想要使用地圖,字符串,等等,彷彿它們是Iterable

我發現的唯一解決方案是定義多個隱式轉換爲pimp類型。

UPDATE

這裏的問題是在從所提供的參數,它不會出現有型參數推斷所述類型參數P。你基本上正在嘗試爲類型構造函數做一些操作,而提取器會爲常規構造函數做些什麼,而多態性正在阻礙它。

編輯過的示例工作,因爲不需要這種特殊的推斷,美中不足的是,你現在只能返回Iterable,因此失去很多的CanBuildFrom

的利益。如果這不是一個問題,那麼它的一個更簡單的解決方案,所以滾動它。

否則,您需要針對您想要皮條客的每種可能類型的不同含義。

更新2

考慮設法確定是否Range是一個有效的參數時,編譯器可能會如何處理您的不同表現:

以1:

implicit def iter2foo[P, S[X] <: Iterable[X]](s : S[P]) = new Foo[P,S](s) 
  • S是一種更高級的類型,種類* => *
  • Range是一個簡單的類型,那種*
  • 的種類不匹配所以它是無效

以2:

implicit def iter2foo[P, S <: Iterable[P]](s : S) = new Foo[P,Iterable](s) 
  • 同樣的問題,S仍然是一種* => *爭執不符

取3:

implicit def iter2foo[P](s : Iterable[P]) = new Foo[P,Iterable](s) 
  • 有過提供的參數,Iterable[P]是簡單類型種類*
  • Range遍這個第一關
  • 第二檢查是RangeIterable[P]爲一個子類一些P
  • 它與P推斷爲Int
  • 編譯器是幸福的,所有的推論,邊界檢查等已成功
+0

它不應該爲'String'工作(因爲它是唯一可見的是'Seq'),但它應該'Map','範圍「和其他可憐的生物。如果這是一個問題,爲什麼第二個版本不起作用?我怎樣才能定義第二個隱式轉換,它捕獲所有不帶類型參數的'Iterable'的子類型? – Raphael 2011-03-03 18:04:19

+0

@update:我明白了。然而,我不清楚爲什麼推理者不考慮超類型。由於你不能用不同的參數(?)擴展相同的特徵,這應該產生明確的結果。至於我更簡單的版本,它創建了正確的(動態)類型的集合,但只能靜態地輸入「Iterable」。那就是你得到'CanBuildFrom'的「底層」優勢,但不是打字優勢。 – Raphael 2011-03-04 11:13:41

+0

我已經擴展了我的答案,以更詳細地解釋。 – 2011-03-04 11:36:27