| « | Computer Savings Time » |
Here are some statistics from my ongoing translation from Scala to Java:
| units | Scala | Java | Ratio |
|---|---|---|---|
| bytes | 34397 | 53446 | 64.36% |
| words | 3766 | 5787 | 65.08% |
| lines | 838 | 1596 | 52.50% |
There are some problems with the comparison; there is one file not comparable, the words metric isn't much use, and you can see my vertical space bias show up in the line count ratio compared to the other two ratios. Also the bytes comparison is not quite fair since I haven't copied the comments from the Java code. So in reality the savings for Scala is probably around 70% if you do a straight port, stopping only to convert obvious uses of exists or find. Of course if you actually do functional programming in it the advantage would increase because Java's syntax is so bad for functional programming.
Scala's syntax is nice both for functional programming AND imperative programming, which no other language really gets, as far as I know (Haskell is decent, though). I no longer have a feeling of whether something is "C-like" enough to be palatable to the mainstream syntactic palate. Here's a sample; maybe you can tell me:
package heiberg
import scala.collection.jcl.SetWrapper
abstract class FeatureOperation(feature: Feature, ocp: boolean)
extends Operation(feature, ocp, false)
class DeleteFeature(feature:Feature) extends FeatureOperation(feature, false) {
override def test(rep : Representation) = //parens are necessary for emacs
(rep.featureTypeSet containsKey featureType) &&
((rep.featureTypeSet get featureType).asInstanceOf[FeatureTokenSet] contains feature)
override def apply(rep : Representation) = {
for(association <- new SetWrapper[Association]
{override def underlying = rep.rootAssociations};
if association.child==feature)
rep.remove(association)
rep.floatingFeatures remove feature
rep remove feature
}
override def toString = "delete(" + feature + ")"
}
class InsertFeature(feature: Feature) extends FeatureOperation(feature, true) {
override def test(rep : Representation) = {
rep.operationsTried add this
val tokens = (rep.featureTypeSet get featureType).asInstanceOf[FeatureTokenSet]
tokens==null || !(tokens contains feature)
}
override def apply(rep : Representation) = rep add feature
override def toString = "insert(" + feature + ")"
}
There are three classes in this file because they are all tiny and I got tired of switching between 12 windows in Emacs alone. Of course you could put semi-colons in there if you want. But they are optional so I leave them off. Also notice that brackets are optional for *anything* that consists of only one expression, not just if and loops. I leave those off aggressively, but feel guilty about doing it.
You will also notice a bunch of SetWrapper noise. You can use Java data structures, but they don't support map/filter/flatMap, and hence don't support for-comprehensions. In other words, you have to drop back to pre-1.5 code (ugh), or wrap Java data structures in a Scala wrapper. I actually like this for porting because it's explicit. If I were developing a truly mixed-code project it might get annoying. The casts are in there for the same reason--in general Java 1.5 and Scala 2.6 see each other as if operating from older specifications of the JVM. The two generic systems are similar, but both are compiler-only so they can't see each other.
Here is some of the weirder syntax. The default constructor is built in to the class declaration--you can create additional ones using def this(...). And there is this optional syntax for unary method calls:
obj.method(arg) obj method arg // nested: obj.method(arg).chained(arg) (obj method arg) chained arg
Naturally the second is what I am familiar with and so I use it more (though for me the two syntaxes are in free variation). However, I imagine it's confusing or ugly for Java people. The optional parens are designed, I think, to provide a Ruby-like syntax for DSLs. Which is important if you want to be trendy, eh?
array each { x =>
println(x)
println("Ruby is so cool!")
println("oh wait...")
}
Edit: A great majority of those comments are things like //end for, though, which I don't feel a bit guilty about skipping.