Почему неявное преобразование Scala здесь работает с двумя аргументами, а не с одним? - PullRequest
3 голосов
/ 03 мая 2011

На объекте 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)), который работает в случае одной пары.

Почему apply(pairs : (String, ValueBase)*) не работает только с одной парой?

(Бонусный вопрос: добавление дополнительного apply (), кажется, решает проблему - есть ли лучшее решение? Что-то не так с этим решением?)

Вот полный скомпилированный пример, упрощенный от моего реального кода, чтобы попытаться показать минимальную проблему.

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"))
}

1 Ответ

4 голосов
/ 03 мая 2011

Это происходит, как объяснил Даниэль Собрал в комментарии, потому что «компилятор видит один аргумент, два применяемых метода, которые могут принимать один аргумент, и переданный аргумент не подходит ни одному. Затем сдается , учитывая что-либо еще, потому что не знает, какой метод попробовать. Если передано два аргумента, то один apply отбрасывается, и компилятор ищет неявные преобразования, которые заставляют другой работать. ”

(Помните, что один 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")
}

Не совсем то, на что вы надеялись, я знаю ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...