-
Notifications
You must be signed in to change notification settings - Fork 6
Scalaxy Compilets
Scalaxy processes compilets.
Compilets are a way to achieve a very useful subset of what compiler plugins can do, in a highly natural way (made possible by the upcoming Scala 2.10 macros and expression trees)
- Intuitive, declarative patterns (just write the Scala code you're trying to match)
- Define AST rewrites to make Scala or DSLs faster, or to add new functionality (without any knowledge of compiler internals)
- Add your own errors or warnings to enforce good library / corporate practices
- Pluggable architecture : any library can embed its own compilets, bundled within its regular JAR (will be detected by Scalaxy's compiler plugin and processed during compilation)
Target audience :
- Library developers who want their DSLs to be fast and/or need to provide additional errors / warnings during compilation (see this post about the cost of implicit conversions)
- Any Scala developer who wants faster Scala out-of-the box, or who uses libraries that leverage the Scalaxy compiler plugin
- Scala Compiler developers who want to prototype optimizations
Compilets are just objects with methods that define match actions.
A match action applies to a pattern and can be :
-
a replacement
def replace666By777 = replacement(666, 777)
-
a compilation warning
def warn666 = warn("I don't like this number") { 666 }
-
a compilation error
def error666 = fail("Are you satanist ?") { 666 }
-
a conditional action, which depends on the actual contents of the AST
import scalaxy.matchers._ // IntConstant and others def additive666(a: Int, b: Int) = when(a + b)(a, b) { case IntConstant(aValue) :: IntConstant(bValue) :: Nil if (aValue + bValue) == 666 => error("You sneaky b*****d !") case _ => warn("Beware of adding integers : might be deprecated in the future") }
Of course, you are not restricted to constants : it's possible to add as many variables (and type variables) as you wish to your compilet methods (see examples below).
-
Simple for loops are notoriously not optimized yet in Scala (the ScalaCL project aimed at fixing that, amongst other things). It's easy to have them rewritten to equivalent while loops :
def simpleForeachUntil[U](start: Int, end: Int, body: U) = replace( for (i <- start until end) body, { var ii = start while (ii < end) { val i = ii body ii = ii + 1 } } )
-
Numeric implicits create lots of NumericOps objects, which are not optimized away by the compiler and might degrate performance. It's easy to get rid of them :
import math.Numeric.Implicits._ import Ordering.Implicits._ def plus[T](a: T, b: T)(implicit n: Numeric[T]) = replace( a + b, // Expanded to Numeric.Implicits.infixNumericOps[T](a)(n).+(b) n.plus(a, b) )
-
Some Java APIs have been marked as evil for quite a while : you may want to forbid them in your code base :
def forbidThreadStop(t: Thread) = fail("You must NOT call Thread.stop() !") { t.stop }
- Provide maven shade plugin instructions to use AppendingTransformer for META-INF/scalaxy.compilets
- More auto-tests : test resulting tree equality w/ macro system ? (what phase are macro executed at ?)
- Add
@Optimization
annotation to disable rewrites unless -optimise is set in compiler options - Deploy Scalaxy to Sonatype repo
- Provide usage instructions
- Add SPI-like META-INF/scalaxy declarations mechanism
- Create SBT plugin that creates META-INF stuff
- Add a giter8 template for library designers that want to embed AST rewrites to their libraries
- Add explicit replacement context declaration, and context limitation
- Publish case study (Numeric ?)
- Optimize pattern matching performance with prefix tree (to scale up to many replacements)