Использует ли Scala целевой тип при поиске неявного преобразования?

Я читаю книгу Scala in Depth, глава 5 о имплицитах. Автор говорит об этом на странице 102:

Неявная область видимости, используемая для неявных представлений, такая же, как и для неявных параметров. Но когда компилятор ищет ассоциации типов, он использует тип, который пытается преобразовать из [выделено мной], а не тип, в который он пытается преобразовать.

Тем не менее, несколькими страницами позже он показывает пример с классом complexmath.ComplexNumber. Вы импортируете i, который является ComplexNumber, и вызываете его * метод, который принимает аргумент ComplexNumber.

import complexmath.i
i * 1.0

Чтобы преобразовать 1.0 в ComplexNumber, это находит неявное преобразование, которое было определено так:

package object complexmath {
  implicit def realToComplex(r: Double) = new ComplexNumber(r, 0)
  val i = ComplexNumber(0, 1)    

Но это противоречит первому утверждению, не так ли? Нужно было найти Double => ComplexNumber. Почему это было проверено в пакете complexmath, который является частью неявной области видимости для ComplexNumber, но не для Double?


person Rob N    schedule 12.03.2014    source источник


Ответы (4)


Либо источник, либо цель работают:

object Foo {
  implicit def bar(b: Bar): Foo = new Foo {}
  implicit def foo(f: Foo): Bar = new Bar {}
}
trait Foo
trait Bar

implicitly[Foo => Bar]  // ok
implicitly[Bar => Foo]  // ok

val b = new Bar {}
val bf: Foo = b  // ok
val f = new Foo {}
val fb: Bar = f  // ok

Так что я думаю, что это предложение неверное (?)

person 0__    schedule 12.03.2014
comment
Сначала я подумал, что ваш b: Foo был заменен выводом, искаженным путем вырезания / вставки. - person som-snytt; 13.03.2014
comment
@ som-snytt А, ладно, я немного отредактировал, чтобы было понятнее - person 0__; 13.03.2014

В спецификации говорится о просмотрах:

неявная область видимости равна T => pt.

то есть Function[T, pt], поэтому неявная область видимости включает классы, связанные как с T, так и с pt, источником и целью преобразования.

scala> :pa
// Entering paste mode (ctrl-D to finish)

class B
class A
object A { implicit def x(a: A): B = new B }

// Exiting paste mode, now interpreting.

warning: there were 1 feature warning(s); re-run with -feature for details
defined class B
defined class A
defined object A

scala> val b: B = new A
b: B = B@63b41a65

scala> def f(b: B) = 3 ; def g = f(new A)
f: (b: B)Int
g: Int

scala> :pa
// Entering paste mode (ctrl-D to finish)

class A
class B
object B { implicit def x(a: A): B = new B }

// Exiting paste mode, now interpreting.

scala> val b: B = new A
b: B = B@6ba3b481
person som-snytt    schedule 13.03.2014

Я думаю, вы неправильно понимаете его текст.

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

В указанном вами примере он будет предоставлен пакетом complexmath.

Однако нас здесь интересует определение «подходящего». В случае неявных преобразований компилятор будет искать преобразование из * Double * в ожидаемый тип ComplexNumber.

Другими словами, он будет проверять все преобразования из * Double *, пока не найдет тот, который может преобразовать Double в целевой тип.

Джош, автор, не говорит, что компилятору требуется преобразование, определенное в объекте, связанном с объектом Double. Преобразование можно определить везде.

В этом конкретном случае преобразование определяется в объекте пакета, связанном с объектом ComplexNumber. И это нормально, если объект ComplexNumber «хочет» быть совместимым с Double.

И поскольку использование подразумевает импорт ComplexNumber и, следовательно, импорт пакета 'complexmath', преобразование всегда будет в рамках.

person Renato    schedule 12.03.2014
comment
По поводу вашего последнего абзаца: в книге делается упор на то, чтобы не импортировать complexmath._, поэтому функция преобразования realToComplex недоступна без префикса. Это меняет ваш ответ? Очевидно, что компилятор не просматривает все пакеты в поисках преобразований. - person Rob N; 13.03.2014
comment
Объект пакета "complexmath" импортирован, поскольку импортирован "complexmath.i". Преобразование определяется в объекте пакета как неявное def, поэтому оно будет доступно. Вот как я это понимаю. @jsuereth, можете ли вы это подтвердить? - person Renato; 13.03.2014

Так что это больше о том, что компилятор уже * знает * о выражении.

В примере:

import complexmath.i
i * 1.0

Компилятор сначала смотрит на это:

1) У меня есть тип i, это Complex 2) У меня есть метод i с именем *, он принимает аргумент типа Complex 3) Вы передали мне Int, но мне нужен Complex. давайте посмотрим, есть ли у меня неявное, что дает мне это.

Этот пример работает, потому что метод * определен в Complex.

Надеюсь, это поможет!

person Community    schedule 12.03.2014
comment
Хорошо, суть в шаге 3. realToComplex недоступен в качестве идентификатора без префикса, поэтому на шаге 3 он выполняет поиск в неявной области видимости - но неявной области видимости какого типа (ов)? В книге, как мне кажется, четко указано: Double (т.е. тип from). Но на самом деле, похоже, и Double, и ComplexNumber. Преобразование находится в неявной области ComplexNumber, потому что эта неявная область содержит пакет complexmath, который содержит функцию преобразования. - person Rob N; 13.03.2014
comment
Это зависит от того, что знает компилятор. На этом этапе компилятор знает, что у него есть Int, а для метода * требуется комплекс, поэтому он ищет Int => Conmplex, что означает, что он будет искать как в Int, так и в Complex. Однако если бы у вас было 1.0 * i, известные факты были бы другими. - person jsuereth; 13.03.2014
comment
1.0 * i находит такое же преобразование и компилирует, как и i * 1.0. Может, со времен книги все изменилось. - person Rob N; 13.03.2014
comment
Да, по крайней мере, в этом примере. Я считаю, что сейчас Scala может рассматривать оба типа в выражении, прежде чем искать имплициты, тогда как, как и раньше, он будет смотреть только на левую часть first. Я полагаю, что поиск справа происходит после логики слева и справа, но я не могу вспомнить детали. - person jsuereth; 13.03.2014
comment
Да, также обратите внимание: я написал этот раздел несколько лет назад. К сожалению, последствия тесно связаны с тем, как реализован компилятор. По мере того, как Scala улучшает взаимодействие с пользователем и устраняет ошибки, некоторые аспекты, вызывающие беспокойство, могут улучшиться. Надеюсь выпустить 2-е издание для 2.12, но посмотрим. - person jsuereth; 13.03.2014