-
Notifications
You must be signed in to change notification settings - Fork 690
Macro Expand
expand
is a macro annotation that is used for code generation in Breeze. You don't have to understand it if you want only to use Breeze. It's useful only for contributors.
The basic idea behind @expand is you start with something like:
implicit val foo: Impl[Double, Double] = ???
and then you replace it with
@expand
implicit def foo[@expand.args(Int, Double, Float, Long) T]: Impl[T, T] = ???
which generates code equivalent to:
implicit def foo_Int: Impl[Int, Int] = ???
implicit def foo_Float: Impl[Float, Float] = ???
implicit def foo_Long: Impl[Long, Long] = ???
implicit def foo_Double: Impl[Double, Double] = ???
If you have more than one type parameter with expand.args
, the full cross product gets generated.
There are three remaining pieces to expand
: @expand.valify, @expand.sequence, and @expand.exclude. expand.valify
just replaces the def with a val, which you can only do if there are no type or value arguments left after expansion. sequence
is for associating values with expanded type parameters, and exclude
is for omitting particular configurations from the cross product. For example:
@expand
@expand.exclude(Double, Double)
implicit def foo[@expand.args(Int, Double) T,
@expand.args(Int, Double) U]
(implicit @expand.sequence[T](0, 0.0) zero: T): Impl[T, U] = zero
will generate
implicit def foo_Int_Int: Impl[Int, Int] = 0
implicit def foo_Int_Double: Impl[Int, Double] = 0
implicit def foo_Double_Int: Impl[Double, Int] = 0.0
The Double,Double version is exclude
d, so it doesn't get generated.
sequence
sees that you want to associate zero
's value with T
, and so zero
is replaced with 0 if T is Int and 0.0 if T is Double. Note that it is the ordering that matters. If I had written @expand.sequence[T](0.0, 0) zero
, then zero
would be 0.0 if T is Int, and 0 if T is Double.
One last trick is that if a sequence
d value is used as a function, then the corresponding function is inlined. So...
@expand
def bar[@expand.args(Int) T](@expand.sequence[T]({_ + _}) op: (T,T)=>T) = {
op(3,4)
}
is
def bar_Int = 3 + 4
I should note that the declared type of an @sequenced
parameter does not matter at all. I could have declared op
's type to be BLAH, and everything would have worked fine. I usually declare it to be something reasonable in code (like in the examples), as a kind of skeuomorphism.
As for the relationship between @expand and @specialized: They overlap but they both can coexist. @expand tries to do much less than @specialized, which is to say, expand doesn't try to make the specialized classes inherit from the non-specialized; it just does name mangling and code generation. It also (currently) only works on defs. I'll probably extend it to classes at some point.
Another future direction for expand or an expand-like macro is to make it generate classes and methods for variable arity's (e.g. things like TupleN)
Breeze is a numerical processing library for Scala. http://www.scalanlp.org