2011-05-03 47 views
3

在對象ValueSet上,我有apply(pairs : (String, ValueBase)*)以及從Int和String到ValueBase的隱式轉換。如果我申請ValueSet("a" -> 1, "b" -> 2)那麼(String, Int)對轉換爲(String, ValueBase),它工作正常。如果我只申請一對,ValueSet("a" -> 1)則表示沒有申請(String,Int)的超載,即它不會隱式轉換。我可以通過添加apply[V <% ValueBase](p : (String, V))來解決這個問題,它可以在一對情況下工作。爲什麼Scala隱式轉換工作有兩個參數,但沒有一個?

爲什麼apply(pairs : (String, ValueBase)*)只能用一對?

(獎金的問題:增加額外的應用()似乎解決的問題 - 有沒有更好的解決辦法是有什麼問題該解決方案?)

下面是一個完整編譯的例子,從我的實際代碼簡化試圖展示最小的問題。

class ValueBase 

case class ValueInt(val value : Int) extends ValueBase 
case class ValueString(val value : String) extends ValueBase 

case class ValuePair(val key : String, val value : ValueBase) 
case class ValueSet(val value : List[ValuePair]) extends ValueBase 

object ValueSet { 
    def apply(pairs : (String, ValueBase)*) : ValueSet = { 
     ValueSet(pairs.map(p => ValuePair(p._1, p._2)).toList) 
    } 

    /* Commenting out this apply() means single-pair 
    *  ValueSet("a" -> 1) 
    * will not compile, error is: 
    *  overloaded method value apply with alternatives: (value: List[ValuePair])ValueSet <and> (pairs: (String, ValueBase)*)ValueSet cannot be applied to ((java.lang.String, Int)) 
    * Why does (String,Int) implicit convert to (String,ValueBase) if there are two args but not if there's one? 
    * Why do I need this apply()? 
    */ 
    def apply[V <% ValueBase](p : (String, V)) : ValueSet = { 
     ValueSet(List(ValuePair(p._1, p._2))) 
    } 
} 

object Sample { 
    implicit def int2value(i : Int) = ValueInt(i) 
    implicit def string2value(s : String) = ValueString(s) 

    /* These samples show the goal, to construct the sets 
    * in a nice Map-literal sort of style 
    */ 
    val oneInt = ValueSet("a" -> 1) 
    val oneString = ValueSet("b" -> "c") 
    val twoInt = ValueSet("d" -> 2, "e" -> 3) 
    val twoTypes = ValueSet("f" -> 4, "g" -> "quick brown fox") 

    /* Taking ArrowAssoc out of the picture and typing "Pair" 
    * explicitly doesn't seem to matter 
    */ 
    val oneInt2 = ValueSet(Pair("a", 1)) 
    val twoTypes2 = ValueSet(Pair("f", 4), Pair("g", "quick brown fox")) 
} 

回答

4

出現這種情況,由丹尼爾·索布拉爾在評論解釋說,因爲「編譯器看到一個說法,兩名申請這可能需要一個參數的方法,並且參數傳遞配合都不是。然後放棄考慮其他任何事情,因爲它不知道要嘗試哪種方法。如果傳遞了兩個參數,則丟棄一個應用並且編譯器查找使另一個工作的隱式轉換。「

(請記住,編譯器會自動定義一個apply,因爲您正在定義case class。 )

如果你寫這個代替,隱式轉換工作:

object ValueSet { 
    def fromPairs(pairs: (String, ValueBase)*): ValueSet = { 
    ValueSet(pairs.map(p => ValuePair(p._1, p._2)).toList) 
    } 
} 

object Sample { 
    implicit def int2value(i: Int): ValueInt = ValueInt(i) 
    implicit def string2value(s: String): ValueString = ValueString(s) 

    /* These samples show the goal, to construct the sets 
    * in a nice Map-literal sort of style 
    */ 
    val oneInt = ValueSet.fromPairs("a" -> 1) 
    val oneString = ValueSet.fromPairs("b" -> "c") 
    val twoInt = ValueSet.fromPairs("d" -> 2, "e" -> 3) 
    val twoTypes = ValueSet.fromPairs("f" -> 4, "g" -> "quick brown fox") 
} 

不正是你所希望看到的,我知道...

+0

有趣。我不明白爲什麼編譯器定義的apply()會混淆這個: - /它似乎不像Pair(「a」 - > 1)應該有任何方式成爲List [ValuePair],所以編譯器定義不應該使用apply()...是否給出兩個single-arg apply()重載的問題,編譯器會放棄隱式轉換?但是爲什麼添加額外的apply()修復它 - 可能是因爲它不需要隱式轉換來匹配超載,這是由於「<%」? – 2011-05-03 14:31:04

+0

@Havoc Jean-Philippe將它擊中。這裏的問題是編譯器會看到一個參數,兩個'apply'方法可能需要一個參數,並且傳遞的參數都不適合。它然後*放棄*考慮其他任何事情,因爲它不知道要嘗試哪種方法。如果傳遞了兩個參數,那麼放棄一個'apply'並且編譯器尋找使另一個工作的隱式轉換。 @Jean,你能用這些信息補充答案嗎? – 2011-05-03 14:57:29

+0

@浩瀚我不完全知道。 Scala規範重載分辨率的頁面並不是一個簡單的部分。不知道爲什麼額外的'apply'修復它,但請記住'T <%S'的視圖是代表從'T'到'S'轉換的隱式參數的縮寫,所以它也是在編譯時需要隱式查找。 – 2011-05-03 14:57:39

相關問題