6

私はこのようなことをしたい:

def assuming[A](condition: => Boolean)(f: => A): A = {
  require(condition, /* print source-code of condition */)
  f
}

使用例:

def fib(n: Int) = n match { // yes, yes, I know this is not efficient
  case 0 => 0 
  case 1 => 1
  case i => assuming(i > 0) { fib(i-1) + fib(i-2) }
}

たとえば、 を呼び出した場合、またはfib(-20)のようなメッセージで例外をスローしたいAssertion failed: -20 > 0Assertation failed: i > 0

4

3 に答える 3

5

次のドキュメントを参照しましたか。

http://www.scala-lang.org/api/2.11.0/scala-reflect/#scala.reflect.api.Printers

scala> show(q"-1 < 0")
res6: String = -1.$less(0)

scala> showCode(q"-1 < 0")
res7: String = (-1).<(0)

あるいは、ソース位置を使用してスニペットを集めて印刷する人もいます。

于 2014-04-24T06:45:25.740 に答える
5

おい、アサート マクロは、マクロの使い方を学ぶために実装する基本的なユース ケースの 1 つではありませんか?

そうですね、私もそう思いました。

私の他の回答の「スニペットを収集する」とは、 specs2 がs2 マクロで何をするかを意味していました。

または、 expecty の私の変種のぼったくりのように、任意の表現を行うこともできます。

あなたの例をREPLに数行で入力すると思いました。結局のところ、条件を表すツリーに対応するソースからスニペットを印刷しようとしているだけです。

もっと簡単なことは何ですか?

もちろん、 の方が簡単ですが-Yrangepos、位置を仮定できます。

興味を失う前にどこまで到達したかを共有したいと思います.

人々 (たとえば、vox paulpuli である paulp) は、"キーボードで入力したソース" を表す添付ファイルをツリーに持つことを望んでいます。達成。

述語pに範囲位置がないようです。したがって、もう 1 つの考えは、2 番目のパラメーター リストの括弧であるマクロ アプリケーションの開始を知っているため、ソースを逆方向に作業して、最初のパラメーター リストの閉じ括弧と一致させることができるということです。

それが示すshowCodeような条件付きの場合、きれいに折りたたまれているため、役に立たないことに注意してください。10 < 5false

object X {
  import reflect.macros.blackbox.Context
  def impl[A: c.WeakTypeTag](c: Context)(p: c.Expr[Boolean])(body: c.Expr[A]) = {
    import c.universe._
    def treeLine(t: Tree): String = lineAt(t.pos)
    def lineAt(pos: Position): String = if (pos.isRange) pos.lineContent.drop(pos.column - 1).take(pos.end - pos.start + 1) else "???"
    val msg =
      if (p.tree.pos.isRange) {  // oh, joy
        treeLine(p.tree)
      } else {
        /*
        Console println s"content ${p.tree.pos.lineContent}"
        Console println s"column ${p.tree.pos.column}"  // alas, that's the column of the point of the top of the tree, e.g., < in "a < b".
        val len = body.tree.pos.start - p.tree.pos.start
        p.tree.pos.lineContent drop (p.tree.pos.column - 1) take len
        */
        // OK, I get it: positions are a big mystery. Make woo-woo ghost noises.
        // What we do know is the start of the apply, which must have a close paren or brace in front of it to match:
        // apply(condition)(body)
        showCode(p.tree)
      }
    q"require($p, $msg) ; $body"
  }
  def x[A](p: Boolean)(body: =>A): A = macro X.impl[A]
}

このように範囲の広い位置を取得することが私に起こりました:

object X {
  import reflect.macros.blackbox.Context
  def impl(c: Context)(p: c.Expr[Boolean]) = {
    import c.universe._
    def lineAt(pos: Position): String = if (pos.isRange) pos.lineContent.drop(pos.column - 1).take(pos.end - pos.start + 1) else "???" 
    val msg = lineAt(c.macroApplication.pos)  // oh, joy
    q"require($p, $msg) ; new { def apply[A](body: =>A): A = body }"
  }
  def x(p: Boolean): { def apply[A](body: =>A): A } = macro X.impl
}

それは使用法に近いですx(10 < 5)(println("hi")): requirement failed: (10 < 5)(p。エラーのマージン。

于 2014-05-01T11:09:06.630 に答える
1

Scala 2.11.x を使用している場合、最善の方法はshowCodeメソッドです。このメソッドは、任意の Scala ツリーを正しく出力します。例えば:

scala> import reflect.runtime.universe._
import reflect.runtime.universe._

showCode(q"3.14 < 42")
res1: String = 3.14.<(42)

以前のバージョンの Scala では、show正確性を保証しないメソッドを使用する必要がありました。

scala> show(q"3.14 < 42")
res2: String = 3.14.$less(42)

このメソッドshowCodeは正確さを念頭に置いて設計されているため、必ずしも美しいコードが出力されるとは限りません。美しさが重要な場合は、Scala に貢献するかPrinters、独自のプリンターを作成することができます。Scala ツリーのもう 1 つの興味深いプリンターは、PrettyPrinterScala Refactoring のプリンターです。

于 2014-05-01T18:23:45.823 に答える