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.
My observation is that no matter what language it is written in software tends to evolve towards a level of complexity that is determined by the culture of the people writing it rather than the language. Every project uses up its complexity budget one way or another. The budget is not determined by the language, but by the culture and skill of the people doing the work.
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.
I've found that the best thing you can do with a bad programmer is to let them write the code in whatever potentially inferrior way they think it should be written, in contrast to trying to force them to write it in some better way that they don't understand. It's always better for them to understand what they are coding and why they are coding it that way.
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.
You may disagree, but the assertion is correct. Java was deliberately designed to restrict what programmers could do. The belief was that this would help maintainability, and particularly when trying to get large teams to cooperate.
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.
The part I was disagreeing with is that Scala gives programmers more "rope to hang themselves with". You may be correct that restricting what programmers could do was a goal of Java, but writing Java in practice gives a tremendous number of opportunities to hang oneself. Apologies for not being more clear.
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.
I've witnessed this first hand at place I've worked! When all you know is Java, somehow an XML-based solution starts to look really appealing. I wouldn't know. It's been such a long time since all I knew was Java. But apparently, when you want a a more flexible language but don't want to spend the effort actually learning one, coding up a brand new DSL with XML syntax seems like a great idea! Hopefully the proliferation of better languages on the JVM will end this practice gradually, but in the end it will depend on a new generation of programmers being more educated on basic principles in computer science.
Scala's traits come nowhere near the functionalities offered by dependency injection frameworks such as Guice. When Scala encounters the need for DI, it's very likely that it will roll out a framework that will look a lot like Guice.
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.
Well, for a big enough project something like Guice will become necessary sooner or later. However, for smaller programs traits are simpler to use and get one 80% of Guice's functionality--and it's all built into the language. Traits are relatively easy to understand that any reasonably competent developer should be able to easily reason about. I don't see why a Scala-native DI framework wouldn't be able to leverage this existing, simple solution to build the last 20%.
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.
All fair points, but I'm really curious to see what you did with traits that has anything to do with dependency injection. DI is essentially a runtime activity while traits are just a more granular way of assembling your objects together at compile time. I just don't see the intersection between the two concepts.
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.
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.