Существует значительная разница в том, как Scala разрешает неявные преобразования из "Модели магнитов" для неперегруженных и перегруженных методов.
Предположим, что существует признак Apply
(вариация "Магнетического узора" ), реализованный следующим образом.
trait Apply[A] {
def apply(): A
}
object Apply {
implicit def fromLazyVal[A](v: => A): Apply[A] = new Apply[A] {
def apply(): A = v
}
}
Теперь мы создаем черту Foo
, которая имеет единственный Apply
, который принимает экземпляр Apply
, поэтому мы можем передать ему любое значение произвольного типа A
, поскольку существует неявное преобразование из A => Apply[A]
.
trait Foo[A] {
def apply(a: Apply[A]): A = a()
}
Мы можем убедиться, что он работает так, как ожидалось, с помощью REPL и этот обходной путь для кода de-sugar Scala.
scala> val foo = new Foo[String]{}
foo: Foo[String] = [email protected]
scala> showCode(reify { foo { "foo" } }.tree)
res9: String =
$line21$read.foo.apply(
$read.INSTANCE.Apply.fromLazyVal("foo")
)
Это работает отлично, но предположим, что мы передаем сложное выражение (с ;
) методу Apply
.
scala> val foo = new Foo[Int]{}
foo: Foo[Int] = [email protected]
scala> var i = 0
i: Int = 0
scala> showCode(reify { foo { i = i + 1; i } }.tree)
res10: String =
$line23$read.foo.apply({
$line24$read.`i_=`($line24$read.i.+(1));
$read.INSTANCE.Apply.fromLazyVal($line24$read.i)
})
Как мы видим, неявное преобразование применялось только к последней части сложного выражения (т.е. i
), а не ко всему выражению. Итак, i = i + 1
была строго оценена в тот момент, когда мы передаем ее методу Apply
, чего мы не ожидаем.
Хорошие (или плохие) новости. Мы можем сделать scalac
для использования всего выражения в неявном преобразовании. Таким образом, i = i + 1
будет оцениваться лениво, как и ожидалось. Для этого мы (удивляем, удивляем!) Добавляем метод перегрузки Foo.apply
, который принимает любой тип, но не Apply
.
trait Foo[A] {
def apply(a: Apply[A]): A = a()
def apply(s: Symbol): Foo[A] = this
}
И затем.
scala> var i = 0
i: Int = 0
scala> val foo = new Foo[Int]{}
foo: Foo[Int] = [email protected]
scala> showCode(reify { foo { i = i + 1; i } }.tree)
res11: String =
$line28$read.foo.apply($read.INSTANCE.Apply.fromLazyVal({
$line27$read.`i_=`($line27$read.i.+(1));
$line27$read.i
}))
Как мы видим, все выражение i = i + 1; i
сделало его под неявным преобразованием, как ожидалось.
Итак, мой вопрос: почему? Почему область применения неявного преобразования зависит от того, есть ли в классе перегруженный метод.