/ / Пошук неявних визначень методів у контексті макросів - масштаб, макроси, масштаб-макроси

Пошук неявних методів визначення в макроконтексті - scala, macros, scala-macros

Я розумію основну концепцію макросів у Scala, але наразі не можу виконати цю (просту?) Роботу:

  • Знайти все неявне defs /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, це рішення не надає вам наслідків від супутнього об'єкта.