Каждый раз, когда у вас есть вопросы, как это, хорошее место, чтобы начать это с помощью Reflection 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)
Так ev
предоставленный Predef.$conforms
, неявный метод, который даст вам экземпляр A <:< A
для любого A
, где <:<
распространяется Function1
.
Так что это одна подсказка. Выяснение остальных требует немного подумать о типе вывода. Когда вы звоните same(2)
, компилятор выясняет, что выражение 2
имеет тип Int
и сообщает, что U
- Int
. Затем нужно выяснить, что такое T
, и для этого он ищет неявные функции от Int
до x
для некоторого типа x
.
Это где $conforms
приходит. Это единственный такой метод в области видимости, поэтому компилятор выбирает его, что означает, что ev
имеет тип Int => Int
и T
должен быть Int
, и вы сделали.
Чтобы расширить ответ @TravisBrown, вы также можете выполнить: 'same [Long, Int] (2)' и увидеть, что он все еще компилируется, потому что 'Int' имеет неявное имя' int2long', определенное на нем, что эта работа. Когда такая магия случается, всегда думайте о том, какие импликации попадают в сферу действия для вас, без явного запроса их –
. Спасибо Yuval, запутанная часть была на каком основании компилятор вывел 'T' (до того, как он искал неявный). Это моя первая кисть с неявным методом $ соответствия. – Samar