-
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 (in progress) : 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 degrade 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 }
Scalaxy is a work in progress.
The proof of concept works fine, with very slim test coverage and some cases left aside in the code (cf. TODOs in PatternMatchers.scala
).
Also, the code currently on GitHub is compiled against Scala 2.10-M2, which is very different from 2.10-M3 (got lots of merges from the scalamacros / Kepler project) : work is underway to adapt Scalaxy's code, but since Scala trunk is a moving target (especially with respect to macros), there are still some blocking points :-S
If you want to play with Scalaxy, you'll have to add your compilets to the hard-coded list of compilets in src/main/scala/scalaxy/plugin/ScalaxyPlugin.scala
(plan is to add an extension mechanism soon, though).
- Use function bodies (functions) with clean variables instead of non-hygienic flat blocks (changes semantics of function matches and requires slight internal representation change + "functionizer" component (looks external variables up...))
- 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)