Почему механизм вывода типов Scala терпит неудачу с этим набором неявных аргументов, включающих параметризованные типы?

Я хотел бы определить метод, параметризованный типом T, поведение которого зависит от того, какой неявный аргумент можно найти для типа Box[T]. В следующем коде этот метод определен как foo. При вызове с foo[Int] или foo[String] он без проблем вернет 1 или "two", как и ожидалось.

Где все становится странным, так это в панели методов. Он определяется как возвращающий Int, но вместо foo[Int] у меня есть только foo. Я надеялся, что компилятор сделает вывод, что T должен иметь тип Int. Он этого не делает и вместо этого терпит неудачу:

bash $ scalac Code.scala 
Types.scala:15: error: ambiguous implicit values:
 both value one in object Main of type => Main.Box[Int]
 and value two in object Main of type => Main.Box[java.lang.String]
 match expected type Main.Box[T]
 def bar: Int = foo
             ^
one error found

Что вызывает эту ошибку? Замена foo на foo[Int] компилируется нормально. Более простая ситуация, когда нет типа Box[T], также прекрасно компилируется. Этот пример также приведен ниже и использует argle и bargle вместо foo и bar.

object Main extends Application {

  case class Box[T](value: T)

  implicit val one = Box(1)
  implicit val two = Box("two")

  def foo[T](implicit x: Box[T]): T = {
    x.value
  }

  // does not compile:
  // def bar: Int = foo

  // does compile
  def bar: Int = foo[Int]

  println(bar)
  // prints 1

  // the simpler situation where there is no Box type

  implicit val three = 3
  implicit val four = "four"

  def argle[T](implicit x: T): T = x
  def bargle: String = argle

  println(bargle)
  // prints "four"

}

Что происходит в этом фрагменте, что вызывает такое поведение? Как насчет этого взаимодействия неявных аргументов, вывода типов и стирания, вызывающего проблемы? Есть ли способ изменить этот код так, чтобы строка def foo: Int = bar работала?


person toddaaro    schedule 02.07.2012    source источник


Ответы (2)


Кто-то еще должен будет объяснить, почему механизм вывода типов не может справиться с этим случаем, но если вы хотите очистить свой код, вы, вероятно, могли бы сделать это:

object Test extends App {

  case class Box[T](value: T)

  implicit val one: Box[Int] = Box(1)
  implicit val two: Box[String] = Box("two")

  def foo[T : Box]: T = implicitly[Box[T]].value  
  val bar = foo[Int]  
}

Обратите внимание, что:

  1. Я удалил аннотацию типа из bar, так что вы просто указываете тип один раз (только в другом месте, чем вы хотели)
  2. Я использую App вместо устаревшего Application
  3. Использование привязки к контексту в сигнатуре типа foo
person Dave L.    schedule 03.07.2012
comment
Раньше я не использовал контекстные границы. Я не уверен, что это работает в моей реальной ситуации, но это, безусловно, интересный инструмент, о котором нужно знать. - person toddaaro; 04.07.2012
comment
Я пытался бороться с границами контекста, чтобы получить желаемое поведение, но не смог. Моя основная цель - сделать так, чтобы foo не требовалось указывать T при использовании, если это можно вывести. Однако границы контекста кажутся чище, чем второй список аргументов. - person toddaaro; 05.07.2012

Это может быть связано с SI-3346, хотя там есть неявные аргументы для неявных преобразования, и здесь у вас есть один неявный.

person Daniel C. Sobral    schedule 03.07.2012
comment
Спасибо за указатель. Похоже, что это может быть связано, но мне придется еще немного поразмыслить над ситуацией. - person toddaaro; 04.07.2012