Scalaマクロのerrorとabortの違い

Scalaのマクロで、コンパイルエラーにしてしまうときは Context.error を使う。

def someMethod_impl(c: Context)(arg: c.Expr[String]): c.Expr[Unit] = {
  // ...

  if (cond) {
    c.error(c.enclosingPosition, "a message")
  }

  // ...
}

でもこれが微妙に使いづらくて、なぜなら戻り値が Unit だから。Unit だと:

val _arg = arg.tree match {
  case Literal(Constant(value: String)) =>
    value
  case _ =>
    c.error(c.enclosingPosition, "a message")
}

とかが出来ません。この場合変数 _argString になってほしいのですが、実際は StringUnit の共通の親である Any になってしまうわけです。

で、登場するのが Context.abort 。こちらは戻り値が Nothing になっているので、上記のようなケースで上手く動作します。

val _arg = arg.tree match {
  case Literal(Constant(value: String)) =>
    value
  case _ =>
    c.abort(c.enclosingPosition, "a message")
}

// 変数 `_arg` はちゃんと `String`

ケースに応じて使い分ければ良さそうですね。