Friday, April 9, 2010

Elidable (remove method calls at compile time)

In Scala 2.8 there is a useful annotation called 'elidable'. This annotation flags a method so that given certain compiler flags all calls to the method will be removed. This is handy if you are writing a logger. The log methods can be annotated so that when compiling for production all log calls below a certain level would be removed from the compiled code. Several of the methods in Predef are annotated with elidable. Specifically, assume, assert and require.

The elidable annotation takes an Int parameter which specifies the priority of the method. The lower the integer the more likely the method would be removed during compilation. The elidable object defines several values that are used in our example.

When compiling with the -Xelide-below , the compiler parameter will remove all calls to elidable methods value and below.

To try the following example copy the example into a scala file (elidable.scala for example) and compile as indicated below:
  1. package example
  2. import scala.annotation.elidable
  3. import scala.annotation.elidable._
  4. object ElidableExamples {
  5.     @elidable(ALL) def all = println("all")
  6.     @elidable(ASSERTION) def assertion = println("assertion")
  7.     @elidable(CONFIG) def config = println("config")
  8.     @elidable(FINE) def fine = println("fine")
  9.     @elidable(FINER) def finer = println("finer")
  10.     @elidable(FINEST) def finest = println("finest")
  11.     @elidable(INFO) def info = println("info")
  12.     @elidable(OFF) def off = println("off")
  13.     @elidable(SEVERE) def severe = println("severe")
  14.     @elidable(WARNING) def warning = println("warning")
  15. }
  16. object Main extends Application {
  17. println("starting")
  18. import ElidableExamples._
  19. all
  20. assertion
  21. config
  22. fine
  23. finer
  24. finest
  25. info
  26. off
  27. severe
  28. warning
  29. println("ending")
  30. assert(false"boom!")
  31. }


Output from scalac elidable.scala && scala example.Main

starting
assertion
off
ending
java.lang.AssertionError: assertion failed: boom!
at scala.Predef$.assert(Predef.scala:93)
at example.Main$.(elidable.scala:34)
at example.Main$.(elidable.scala)
at example.Main.main(elidable.scala)

Output from scalac -Xelide-below 0 elidable.scala && scala example.Main

starting
assertion
config
fine
finer
finest
info
off
severe
warning
ending
java.lang.AssertionError: assertion failed: boom!
at scala.Predef$.assert(Predef.scala:93)
at example.Main$.(elidable.scala:34)
at example.Main$.(elidable.scala)
at example.Main.main(elidable.scala)

Output from scalac -Xelide-below 1000 elidable.scala && scala example.Main

starting
assertion
off
severe
ending
java.lang.AssertionError: assertion failed: boom!
at scala.Predef$.assert(Predef.scala:93)
at example.Main$.(elidable.scala:34)
at example.Main$.(elidable.scala)
at example.Main.main(elidable.scala)

Output from scalac -Xelide-below 3000 elidable.scala && scala example.Main

starting
off
ending

4 comments:

  1. This sounds great, hadn't heard of it, but what does it do to line numbers when attaching to those classes with elided blocks at runtime?

    Patrick

    ReplyDelete
  2. A good question :)

    I have not checked but I would think that it would be a bug if the remaining line numbers did not correctly line up. I think the Scala compiler guys are pretty used to line number tracking

    ReplyDelete
  3. There was a discussion about using class hierarchy to select what should be elided. It's an interesting idea.

    ReplyDelete
  4. At least in Scala 2.8.1, require (in contrast to assert and assume) is not annotated in Predef with @elidable(ASSERTION).

    ReplyDelete