Я розумію основну концепцію макросів у Scala, але наразі не можу виконати цю (просту?) Роботу:
- Знайти все неявне
def
s /val
На даний момент видимий для компілятора для перетворення з заданого типу в інший.
Що я очікував отримати, це List
від Method
предмети або щось подібне. Я вже погрався з enclosingImplicits
але завжди отримуйте порожній список і насправді не знаєте, де шукати далі.
Що мені потрібно зробити, щоб отримати список, який я шукаю?
Відповіді:
2 для відповіді № 1Імпліцитний від типу може бути лише один A
до B
в контексті (або ви отримуєте амбіційний неявний), тому, якщо ви хочете знайти його:
import reflect.macros.Context, scala.language.experimental.macros
def fImpl(c: Context): c.Expr[Unit] = {
import c.mirror._
println(c.inferImplicitValue(typeOf[Int]))
c.universe.reify( () )
}
def f = macro fImpl
scala> f
<empty>
scala> implicit val a = 5
a: Int = 5
scala> f
$line24.$read.$iw.$iw.$iw.$iw.a
scala> implicit val b = 5
b: Int = 5
scala> f //result will be empty, but error printed to the log
error: ambiguous implicit values:
both value a of type => Int
and value b of type => Int
match expected type Int
<empty>
Щоб знайти неявний метод:
def fImpl(c: Context): c.Expr[Unit] = {
import c.mirror._
println(c.inferImplicitValue(typeOf[String => Int]))
c.universe.reify( () )
}
def f = macro fImpl
scala> f
<empty>
scala> implicit def aaa(a: String) = 5
warning: there was one feature warning; re-run with -feature for details
aaa: (a: String)Int
scala> "A" : Int
res10: Int = 5
scala> f
{
((a: String) => $line47.$read.$iw.$iw.$iw.$iw.$iw.$iw.aaa(a))
}
Якщо silent
параметр є false
(true
за замовчуванням), TypecheckException
буде викинуто у разі помилки виводу. Таким чином, ви можете проаналізувати його, щоб знайти список амбіційних наслідків.
P.S. Якщо тип B
невідомий - не існує (задокументованого) способу знайти всі імпліцити за допомогою макросів: openImplicits
/enclosingImplicits
просто шукати наслідки, що матеріалізуються в контексті макророзширення - не для всіх, що існують у контексті. Compiler-plugin може допомогти, але це не так просто тоді.
Якщо ви дійсно вирішите спробувати спосіб "компілятор-плагін" - логіка пошуку імпліцитів реалізована тут. Ось тут Ви можете знайти компілятор Context
(не те саме, що макрос) та його implicitss
поле, яке містить усі імпліцити в контексті (але це не так тривіально, щоб отримати відповідний контекст).
І Я не повинен казати вам але є хитрий та небезпечний хак, щоб зняти з макросу Context
на рівень компілятора і роби те, що хочеш:
scala> def fImpl(c: Context): c.Expr[Unit] = {
| val cc = c.asInstanceOf[reflect.macros.contexts.Context]
| println(cc.callsiteTyper.context.implicitss.flatten)
| c.universe.reify( () )
| }
fImpl: (c: reflect.macros.Context)c.Expr[Unit]
scala> def f = macro fImpl
scala> f //I"ve defined aaaaaaaa etc. implicits while playing with that
List(aaaaaaaa: ?, lllllllllllllllllllllzzzz: ?, lllllllllllllllllllll: ?, lllllllllllllllllllll: ?, aaa: ?, aaa: ?, aaa: ?, aaa: ?, aaa: ?, aaa: ?, b: ?, a: ?, macros: ?, RuntimeClassTag:
У будь-якому випадку, вам доведеться проаналізувати список ImplicitInfo
для отримання наслідків, які ви шукаєте, і це може бути нетривіально, як ви можете бачити з Analizer
"s джерел", але принаймні можна отримати приблизний результат, який може відповідати вашим потребам. Але знову ж таки, краще це зробити дуже дуже дуже обережно, оскільки структури, з якими ви працюєте, змінюються, а методи не є чистими. І, як зауважив @Eugene Burmako, це рішення не надає вам наслідків від супутнього об'єкта.