Newbie Scala Question:
Say I want to do this [Java code] in Scala:
public static double[] abs(double[] r, double[] im) {
double t[] = new double[r.length];
for (int i = 0; i < t.length; ++i) {
t[i] = Math.sqrt(r[i] * r[i] + im[i] * im[i]);
}
return t;
}
and also make it generic (since Scala efficiently do generic primitives I have read). Relying only on the core language (no library objects/classes, methods, etc), how would one do this? Truthfully I don’t see how to do it at all, so I guess that’s just a pure bonus point question.
I ran into sooo many problems trying to do this simple thing that I have given up on Scala for the moment. Hopefully once I see the Scala way I will have an ‘aha’ moment.
UPDATE:
Discussing this with others, this is the best answer I have found so far.
def abs[T](r: Iterable[T], im: Iterable[T])(implicit n: Numeric[T]) = {
import n.mkNumericOps
r zip(im) map(t => math.sqrt((t._1 * t._1 + t._2 * t._2).toDouble))
}
Doing generic/performant primitives in scala actually involves two related mechanisms which scala uses to avoid boxing/unboxing (e.g. wrapping an
intin ajava.lang.Integerand vice versa):@specializetype annotationsManifestwith arraysspecializeis an annotation that tells the Java compiler to create “primitive” versions of code (akin to C++ templates, so I am told). Check out the type declaration ofTuple2(which is specialized) compared withList(which isn’t). It was added in 2.8 and means that, for example code likeCC[Int].map(f : Int => Int)is executed without ever boxing anyints (assumingCCis specialized, of course!).Manifests are a way of doing reified types in scala (which is limited by the JVM’s type erasure). This is particularly useful when you want to have a method genericized on some typeTand then create an array ofT(i.e.T[]) within the method. In Java this is not possible becausenew T[]is illegal. In scala this is possible using Manifests. In particular, and in this case it allows us to construct a primitive T-array, likedouble[]orint[]. (This is awesome, in case you were wondering)Boxing is so important from a performance perspective because it creates garbage, unless all of your
ints are < 127. It also, obviously, adds a level of indirection in terms of extra process steps/method calls etc. But consider that you probably don’t give a hoot unless you are absolutely positively sure that you definitely do (i.e. most code does not need such micro-optimization)So, back to the question: in order to do this with no boxing/unboxing, you must use
Array(Listis not specialized yet, and would be more object-hungry anyway, even if it were!). Thezippedfunction on a pair of collections will return a collection ofTuple2s (which will not require boxing, as this is specialized).In order to do this generically (i.e. across various numeric types) you must require a context bound on your generic parameter that it is
Numericand that aManifestcan be found (required for array creation). So I started along the lines of……but it doesn’t quite work. The reason is that a “generic”
Numericvalue does not have an operation likesqrt-> so you could only do this at the point of knowing you had aDouble. For example:Excellent – now see this purely generic method do some stuff!
Now we can
sqrtthe result, because we have aArray[Double]And to prove that this would work even with another
Numerictype: