#scala #implicit
#scala #неявный
Вопрос:
Я нигде не определил функцию ev
. Тогда как работает приведенный ниже код? Разве имплициты не должны быть определены где-то в области видимости, чтобы их можно было использовать?
def same[T, U](x: U)(implicit ev: U => T): T = {ev(x)}
same(2) // 2
Комментарии:
1. Чтобы расширить ответ @TravisBrown, вы также можете сделать:
same[Long, Int](2)
и посмотреть, что он все еще компилируется, потомуInt
int2long
что для него определен неявный called , который бы заставил это работать. Когда происходит подобное волшебство, всегда думайте о том, какие импликации попадают в область видимости для вас, без явного запроса их2. Спасибо, Ювал, запутанная часть заключалась в том, на каком основании компилятор выводил
T
(до того, как он искал неявное). Это мое первое знакомство с неявным методом $conforms .
Ответ №1:
Каждый раз, когда у вас возникают подобные вопросы, хорошим началом является использование API отражения Scala в REPL, чтобы спросить компилятор, что происходит:
scala> import scala.reflect.runtime.universe.{ reify, showCode }
import scala.reflect.runtime.universe.{reify, showCode}
scala> def same[T, U](x: U)(implicit ev: U => T): T = ev(x)
same: [T, U](x: U)(implicit ev: U => T)T
scala> showCode(reify(same(2)).tree)
res0: String = $read.same(2)(Predef.$conforms)
So ev
предоставляется <a rel="noreferrer noopener nofollow" href="https:///www.scala-lang.org/api/2.11.8/index.html#scala.Predef$@$conforms[A]:<: Predef.$conforms
неявным методом, который предоставит вам экземпляр A <:< A
для любого A
, где <:<
расширяется Function1
.
Итак, это одна подсказка. Для выяснения остального требуется немного подумать о выводе типов. При вызове same(2)
компилятор определяет, что выражение 2
имеет тип Int
, и делает вывод, что U
это Int
так . Затем ему нужно выяснить, что T
есть, и для этого он ищет неявные функции от Int
до x
для некоторого типа x
.
Вот тут $conforms
-то и приходит. Это единственный такой метод в области видимости, поэтому компилятор выбирает его, что означает, что ev
он имеет тип Int => Int
и T
должен быть Int
, и все готово.
Комментарии:
1. Это аккуратный трюк с отражением. Я всегда использую
scalac -Xprint..
для просмотра дерева.2. @YuvalItzchakov Да,
scalac -Xprint
это тоже полезно, но я нахожу это более шумным и менее удобным для автономных примеров, подобных этому.3. Это определенно создает гораздо больше шума. Я обязательно запомню это 🙂