8

これら 2 つの同等の式を Scala マクロに渡すことを想像してみましょう。

  • コンパイラが推測する暗黙の変換:1+"foo"
  • 明示的に呼び出された暗黙の変換:any2stringadd(1)+"foo"

マクロ内でこれら 2 つを区別する方法はありますか?

4

2 に答える 2

3

まず第一に、1 + "foo"実際には暗黙的な変換が行われていないため、このケースは扱いにくいものになります。Intそれ自体には、本当にこの+メソッドがあります(残念ながら)。

したがって、それがあなたのユースケースである場合は運が悪いですが、より一般的に説明していることを行うことは可能です。以下の例では、次の設定を想定しています。

case class Foo(i: Int)
case class Bar(s: String)
implicit def foo2bar(foo: Foo) = Bar(foo.i.toString)

まず、エレガントなアプローチ:

object ConversionDetector {
  import scala.language.experimental.macros
  import scala.reflect.macros.Context

  def sniff[A](tree: _): Boolean = macro sniff_impl[A]
  def sniff_impl[A: c.WeakTypeTag](c: Context)(tree: c.Tree) = {
    // First we confirm that the code typechecks at all:
    c.typeCheck(tree, c.universe.weakTypeOf[A])

    // Now we try it without views:
    c.literal(
      c.typeCheck(tree, c.universe.weakTypeOf[A], true, true, false).isEmpty
    )
  }
}

必要に応じて機能します:

scala> ConversionDetector.sniff[Bar](Foo(42))
res1: Boolean = true

scala> ConversionDetector.sniff[Bar](foo2bar(Foo(42)))
res2: Boolean = false

残念ながら、これには型指定されていないマクロが必要であり、現在マクロ パラダイスでしか利用できません。

2.10 では単純な古いマクロで必要なものを取得できますdefが、これはちょっとしたハックです:

object ConversionDetector {
  import scala.language.experimental.macros
  import scala.reflect.macros.Context

  def sniff[A](a: A) = macro sniff_impl[A]
  def sniff_impl[A: c.WeakTypeTag](c: Context)(a: c.Expr[A]) = {
    import c.universe._

    c.literal(
      a.tree.exists {
        case app @ Apply(fun, _) => app.pos.column == fun.pos.column
        case _ => false
      }
    )
  }
}

そしてまた:

scala> ConversionDetector.sniff[Bar](Foo(42))
res1: Boolean = true

scala> ConversionDetector.sniff[Bar](foo2bar(Foo(42)))
res2: Boolean = false

トリックは、抽象構文ツリーで関数適用が見られる場所を探し、Applyノードとそのfun子の位置が同じ列を持っているかどうかを確認することです。これは、メソッド呼び出しがソースに明示的に存在しないことを示します.

于 2013-03-20T15:53:06.853 に答える
2

これはハックですが、役立つ場合があります。

import scala.reflect.macros.Context
import language.experimental.macros

object Macros {
  def impl(c: Context)(x: c.Expr[Int]) = {
    import c.universe._
    val hasInferredImplicitArgs = x.tree.isInstanceOf[scala.reflect.internal.Trees#ApplyToImplicitArgs]
    val isAnImplicitConversion = x.tree.isInstanceOf[scala.reflect.internal.Trees#ApplyImplicitView]
    println(s"x = ${x.tree}, args = $hasInferredImplicitArgs, view = $isAnImplicitConversion")
    c.literalUnit
  }

  def foo(x: Int) = macro impl
}

import language.implicitConversions
import scala.reflect.ClassTag

object Test extends App {
  def bar[T: ClassTag](x: T) = x
  implicit def foo(x: String): Int = augmentString(x).toInt
  Macros.foo(2)
  Macros.foo(bar(2))
  Macros.foo("2")
}

08:30 ~/Projects/210x/sandbox (2.10.x)$ ss
x = 2, args = false, view = false
x = Test.this.bar[Int](2)(ClassTag.Int), args = true, view = false
x = Test.this.foo("2"), args = false, view = true
于 2013-04-24T06:31:57.493 に答える