Почему этот явный вызов метода Scala позволяет неявно разрешать его?

Почему этот код не компилируется, но успешно компилируется, когда я раскомментирую указанную строку? (Я использую Scala 2.8 каждую ночь). Кажется, что явный вызов string2Wrapper позволяет использовать его неявно с этого момента.

class A {
  import Implicits.string2Wrapper
  def foo() {
     //string2Wrapper("A") ==> "B" // <-- uncomment
  } 
  def bar() {
    "A" ==> "B"
    "B" ==> "C"
    "C" ==> "D"
  }
  object Implicits {
    implicit def string2Wrapper(s: String) = new Wrapper(s)
    class Wrapper(s: String) {
      def ==>(s2: String) {}
    }
  }
}

Редактировать: спасибо за ответы, в том числе ссылку на комментарий Мартина Одерски,

«Неявное преобразование без явного типа результата видно только в тексте, следующем за его собственным определением. Таким образом, мы избегаем циклических ошибок ссылок».

Мне все еще было бы интересно узнать, 1) в чем опасность «ошибок циклических ссылок»? и 2) Почему явный вызов имеет какое-то значение?


person Matt R    schedule 28.04.2010    source источник


Ответы (3)


Явное указание возвращаемого типа string2Wrapper устраняет проблему.

class A {
  import Implicits._

  def bar() {    
    "A" ==> "B"
    "B" ==> "C"
    "C" ==> "D"
  }
  object Implicits {
    implicit def string2Wrapper(s: String): Wrapper = new Wrapper(s)
    class Wrapper(s: String) {
      def ==>(s2: String) {}
    }
  }
}

Определение Implicits перед bar также работает:

class A {
  object Implicits {
    implicit def string2Wrapper(s: String) = new Wrapper(s)
    class Wrapper(s: String) {
      def ==>(s2: String) {}
    }
  }

  import Implicits._

  def bar() {    
    "A" ==> "B"
    "B" ==> "C"
    "C" ==> "D"
  } 
}

Если вам нужно полагаться на неявное преобразование, определенное ниже в текущей области, обязательно аннотируйте его возвращаемый тип. Почти уверен, что это всплывало в списках рассылки раньше, и может быть ожидаемым поведением, а не ошибкой. Но я не могу найти его в данный момент. Я предполагаю, что явный вызов в foo запускает вывод типа возвращаемого типа bar, который затем действителен при вводе содержимого bar.

ОБНОВЛЕНИЕ

Чем опасна ошибка циклического задания?

Тело неявного метода может вызывать метод, требующий неявного преобразования. Если оба они имеют предполагаемый тип возвращаемого значения, вы в тупике. Это не относится к вашему примеру, но компилятор не пытается это обнаружить.

Почему явный вызов имеет значение?

Явный вызов ранее инициирует вывод типа возвращаемого типа неявного метода. Вот логика в Implicits.isValid

sym.isInitialized ||
      sym.sourceFile == null ||
      (sym.sourceFile ne context.unit.source.file) || 
      hasExplicitResultType(sym) ||
      comesBefore(sym, context.owner)

ОБНОВЛЕНИЕ 2

Эта недавняя ошибка выглядит актуальной: https://lampsvn.epfl.ch/trac/scala/ticket/3373

person retronym    schedule 28.04.2010
comment
Это (должно быть) указано. Полп раскопал билет до такой степени совсем недавно. Посмотрим, если не найду... - person Daniel C. Sobral; 28.04.2010

Если бы вы были всего на одну ночь позже, вы бы вместо этого увидели сообщение об ошибке, которое я добавил вчера.

<console>:11: error: value ==> is not a member of java.lang.String
 Note: implicit method string2Wrapper is not applicable here because it comes after the application point and it lacks an explicit result type
           "A" ==> "B"
           ^
<console>:12: error: value ==> is not a member of java.lang.String
 Note: implicit method string2Wrapper is not applicable here because it comes after the application point and it lacks an explicit result type
           "B" ==> "C"
           ^
<console>:13: error: value ==> is not a member of java.lang.String
 Note: implicit method string2Wrapper is not applicable here because it comes after the application point and it lacks an explicit result type
           "C" ==> "D"
           ^
person psp    schedule 28.04.2010

Если вы поставите object Implicits первым, это сработает. Для меня это выглядит как ошибка в логике выполнения нескольких проходов компилятора; предполагается, что он может уйти, не зная о string2Wrapper при компиляции bar. Я предполагаю, что если вы используете его, он знает, что не может уйти, не зная, что такое string2Wrapper на самом деле, фактически компилирует Implicits, а затем понимает, что ==> неявно определено в String.

Изменить: судя по тому, что опубликовал Retronym, возможно, это «функция», а не ошибка. Мне все равно кажется хреновым!

person Rex Kerr    schedule 28.04.2010