Scala is not a better Java
john.freml.in
john.freml.in
Java is deliberately not programmer-orientated. That's the point of using it — it was designed to restrict the kind of trouble programmers can get themselves into. If you're stuck with the JVM, I guess the question is: how much rope do you want to give your programmers? Scala is essentially the opposite answer to Java.
This was an interesting read but I disagree with this assertion. Java's lack of power as a language hasn't meant that people haven't written huge, complex, unmaintainable messes with it. Indeed, I think all the crazy dependency injection frameworks, configuration-by-XML, and other general insanity is due in part to the language's lack of power. Things that are simple in other languages (say, dependency injection, which can be replaced with runtime stubbing in Ruby or mixing in traits in Scala) often require painful and complicated ad-hoc solutions in Java.That said, it is possible to write code in Scala that requires a PhD in category theory to understand (see Scalaz), but there are also many examples of simple, functional, easy to understand code (see stuff that has come out of Twitter, like Kestrel or Nagatti).
I think in practice, working with the modern Java ecosystem gives one at least as much rope to hang oneself as using Scala does.
That Java users have found ways to get around deliberate limitations does not change the fact that this was the original intent of the language design.
Further, it's not like this complexity is something that arose independently of Java itself. One of the most frequently cited examples of super-complicated insanity in the Java world is J2EE, which is a standard and set of libraries created by Sun itself. If the creators of language have managed to create and propagate one of the biggest tarpits available in Java, that reflects poorly on the overall Java ecosystem.
Your point regarding the rope is well taken, the difference being that with Java, the rope lies in the frameworks, while in Scala, it lies both in the frameworks (take a look at Lift) and in the language.
Guice is the result of smart Google engineers who had probably tried all sorts of DI frameworks before and finally developed something nice. It's the result of more than a decade of painful experiences with existing solutions to a real problem.
In conclusion, I think you can hang yourself when using any language. In Scala, when I needed to mock out a dependency for a project, I settled on a fairly simple trait-based approach that was simple and elegant. In Java, I could have rolled by own Factory scheme that would be ugly and unmaintainable, or picked a DI framework that hopefully wouldn't have sucked. In this case I was presented with many fewer opportunities to make a mess than if I had been using Java.
(BTW, you can use Guice in Scala programs right now! And, when you do, it looks way nicer and easier to use than in Java. See http://makeapp.blogspot.com/2009/09/how-to-use-guice-with-sc...)
I have the feeling that you might be confused about what DI is exactly, but I'll happily eat my words if you can point me to a description of what you did, some source code, a blog post or whatever.
My approach was thus: basically, define an abstract trait that specifies methods that return given dependencies. Any class that required dependencies extends this trait and is itself abstract. Then, when you want a real working instance of the class you mix in a concrete implementation of the trait. It's simple, declarative, and it got the job done. Here's an example of the pattern:
abstract class Foo {
def addToMe(i: Int):Int
}
abstract trait FooService { def foo: Foo }
class RealFoo(j: Int) extends Foo {
def addToMe(i: Int) = i + j
}
trait RealFooService extends FooService { def foo = new RealFoo(42) }
abstract class NeedsFoo extends FooService {
def doSomething = foo.addToMe(100)
}
(new NeedsFoo with RealFooService).doSomething
// Int = 142
class TestFoo extends Foo {
def addToMe(i: Int) = i + 1
}
trait TestFooService extends FooService { def foo = new TestFoo }
(new NeedsFoo with TestFooService).doSomething
// Int = 101
Now, this is all done at compile-time, but it really seems to accomplish most of what the first few examples in the Guice documentation do, with similar amounts of boilerplate. Again, I'm sure this approach would fall short in many more complicated scenarios, but it gets you a hell of a lot farther than Java does in solving this problem without adding any extra frameworks. Traits are also not very difficult to reason with.As a simple language, Java tends to result in complicated frameworks. This is a problem because every framework is complicated and different - you can't infer what one is doing by knowing another. Given this, I like having the complexity supported in the language. I'm going to have to deal with it anyway, but at least I can count on the language being the same where ever I go, so I can learn it once and have a reusable skill.
Java forces bad programmers to use patterns that they don't understand. Inevitably, this results in those patterns being applied incorrectly, which is worse than not applying the pattern at all. It's worse still if the programmer thinks they understand the pattern, but doesn't. No type system can guarantee that an API is used correctly on more than a superficial level, nor can it guarantee good architecture or correct behavior.
The best language for a bad programmer to use is a flexible and expressive one. The more easily they can express themselves, the better chance you have of understanding their misconceptions. Forcing their broken ideas into a restrictive language will just mangle them further, producing dailywtf material.
Monkeys are dumb, but a monkey doing calculus is even dumber.
Java is the "oh this is explicit, I just wrote a program the size of the federal register!" language. It is overly simplistic in several areas thereby causing complex workarounds to be implemented to solve real world issues.
As I'm currently taking a look at Android development, I wish there was a simply "better Java". Something that doesn't stray too much from imperative C/Algol/Pascal-like programming, but doesn't require me to use a huge, bloated IDE just to cope with the language features. Better variable declaration, better modules, uniform access principle etc. Fantom looks interesting but -- again -- tries to be more than that (own library, multi-VM etc.).
For instance, you can have a duck type as a function argument that's contract is it implements a particular method signature. In Ruby there is no type type enforcement so you can get runtime errors when you pass in an object that doesn't have the right method, while in Scala the compiler catches it.
You can monkey patch by doing implicit conversions in Scala to a rich type, allowing one to add methods to the Integer class, for example, but if you have to libraries that do the same monkey patch Scala will alert you to ambiguous situations. In Ruby this depends on load order and can cause all kinds of screwy bugs.
I'm still not persuaded that all the gymnastics Scala has to do to allow Ruby-like flexibility really result in a more productive language overall. I need to spend more time with it to be sure but at first blush it really feels like the faustian bargain Scala has had to strike to merge expressive, Ruby-like syntax with strong, static type-checking and java compatibility has resulted in a language too complex for most working programmers.
Clojure seems a lot more elegant and comprehensible.
In my opinion, Scala's aim is to take on Java by providing a much more overall productive language than Java while still maintaining Java's performance. I feel it's very much akin to the goals of C++ versus C. Fortunately, the people behind Scala are well versed in PL theory and make sensible language design decisions, so at this point Scala is in little danger of someone writing an FQA on it any time soon.
A language that was dynamic first, then added the static, is Groovy and Groovy++.
Additionally, JRuby has had a JIT that attempts to do smart things to avoid unnecessary boxing and reflection. They've been rather impeded by the JVM on this front, but JVM 7 looks to open a lot of doors for the JRuby team to remove a lot of unnecessary dynamic dispatch in the execution of Ruby code.
The problem I see with Scala is it reeks of Perl 6's "everything and the kitchen sink" language feature-a-thon. Having learned Clojure and started Scala recently I'm constantly asking myself why Scala code has so much incidental complexity compared to the equivalent Clojure code. Often, it is due to the type system or the excessive number of constructs and syntax available in the language.
Having used Scala for a lot of side projects, the only thing that seems kitchen-sinky to me is native XML support.
Unless this URL (http://clojure.org/java_interop) is out of date, then I fail to see how I'm misreading this section.
"All arguments are passed to Clojure fns as objects, so there's no point to putting non-array primitive type hints on fn args. Instead, use the let technique shown to place args in primitive locals if they need to participate in primitive arithmetic in the body.".
Also, I'm not an expert on JRuby's attempted optimizations, but if I understand correctly they are very limited, much in the same way that Java's escape analysis is. The post By Charles Nutter less than a year ago is illuminating in that regard(http://groups.google.com/group/jvm-languages/browse_thread/t...). Have things changed that significantly since then? I follow his blog regularly and in most benchmarks I can remember him talking about he still compares JRuby to Java running boxed math.
Every programming language is designed to restrict trouble programmers can get into. The key is that they all have different ideas of trouble.
What if they make life easier for you but harder for everyone else on your team?
I'm not saying I disagree with your philosophy, just that there is merit in the other side of the argument as well. I want tools to build powerful abstractions, but I want those powerful abstractions to be easy for everyone to read and, when necessary, explore in detail.
There is a productivity valley between small teams (up to 5-8 people) and large teams (20+). In that valley you get less done than on either side, so it makes no sense to be there.
What this means is that organizations need to make a choice between maximizing what can be done with at most 5-8 people, or else making teams of 20+ people work together smoothly. And the sets of features that are good for either of those goals are frequently bad for the other goal.
Sorry, but I don't agree with that at all. As soon as you have more than one person working on the same part of your code, you incur some degree of overhead. I don't see why the numbers 8 or 20 are special. Are you really claiming that a competent development team of 18 people cannot do more than a competent development team of 8 people?
To co-ordinate teams of many developers, we divide projects into manageable chunks, each with a relatively small number of developers working on them. This requires good abstraction tools, so that developers can see how their contribution fits into the larger picture, can work with the code written by others without having to know it in intimate detail, and can combine all the components into a complete product. Again, I don't see how this picture differs whether you have 8 people or 20 people, unless you're following some sort of surgical team model where you really are trying to get all of the smaller team working on the same code but most of them aren't actually writing that code at all.
See page 229 in Software Estimation for references to research that a medium size project will be produced in the same number of calendar months by a team of 5-8 people or a team of 20-25 people. And it will take LONGER if your team is between those two sizes. Beyond that larger threshold, development also speeded up.
In this case a medium sized project was 10-100 thousand lines of code, and for all the team sizes looked at across all of the projects examined averaged 50-60 thousand lines. I would expect that the small team likely had less code duplication within their project, which would indicate that when measured by functionality, the small team had an even bigger advantage.
Note that this is calendar months to finish the project. If you measure in terms of dollars spent to finish, the most productive team size is 1.
And yes, this is with the larger teams using standard practices such as being divided into smaller, more focused groups. The reason is that the process of doing that requires completely changing how you work, and that results in a significant productivity impact. Once you take that hit you can scale, but you have to take that hit first.
I am willing to accept that I am wrong if the evidence really says I am, but I remain deeply suspicious of these conclusions on the basis of the data you have provided so far. A typical 50-60 KLOC project is the sort of thing I would expect one or two decent developers to put together in less than a year. I don't know why even a team of 8 people would be working on a project that small under most circumstances, never mind a team of 20-25 people. Of course if you go into the low end of the tail then the communications overheads will dominate development time with that sort of team size, but that's hardly typical of a realistic project with a competent development team.
(I keep using qualifiers like "competent", because I don't think this level of performance requires exceptional developers and managers, but I am ruling out clueless management or developers who aren't good enough to function effectively in a team at all. Of course with such people your supervision and communication overheads go through the roof.)
Edit: Never mind. I found the relevant excerpt of the book you mentioned. You are making a completely unfounded generalisation, extrapolating from data taken in a very specific context, with no logical basis whatsoever. I stand by my previous posts.
I still can't grok why I'd need one though. Is DI for Python the product of damaged minds or do they provide some real benefit?
Anyway - if mocking for tests is the major benefit of DI then there some other simpler ways to that. Everything I've read on DI makes it sound like a fairly complex beast.
I have to respectfully disagree with this statement.
It's really easy to write insanely convoluted and unreadable code in any language. The language isn't really the factor there, more the programmer.
The readability and maintainability of software isn't only dependent on the complexity of the language, but also on the complexity of the implementation of a concrete problem.
Yes, the language Java is easy to read and maintain, but their frameworks aren't.
Knowing Java, Haskell, Ruby, and Python already, I found it really easy to jump into Scala with the references listed on the Scala site. If you do have experience with Java/statically-typed FP (or if you're just feeling brave) that's where I'd recommend looking.