Java 8 definitely took some 'oomph' out of mainstream Scala adoption. I do see a lot of Scala adoption at the moment though. Currently it's mostly in ecommerce.
For me Java 8 is too little too late. The things I really miss in Java 8 are:
- pattern matching Not only handling the cases but more importantly deconstructing the object into variables.
- type inferencing To prevent Javas so called type stutter
SessionFactory sessionFactory = createSessionFactory();
. And also to not have to type tedious generic declarations like
Map<String, Observable<JsonNode>> observableMap = stream.collect(mapCollector);
- concise domain modeling using case classes The fact that recently both Swift and C# include a case class equivalent underscores their value I think. Of course case classes are not only concise1 but they're essential for pattern matching and for working with immutable data with their standard support of object copying.
- immutable variables using val There are annotations that can check the same thing in Java. I'd argue that keeping your data immutable is easier to do in Scala and as a result people will do it sooner.
- partial function application Yes, really, it's not only an academic nicety. It turns out that once you're writing non-blocking code you're constantly working with callbacks and that becomes really messy unless you can use partial function application (aka currying). You can do what amounts to currying by returning a function but you quickly lose a lot of your co-developers if you use constructs like that2.
- named parameters and default values Named parameters add so much to the readability of your code.
- tuples Especially for ad hoc returning of multiple results. Of course you want some form of pattern matching to handle the result.
- tail call optimisation for recursive functions Although of course
reduce
in Java 8 streams offers the possibility to do stackless recursive programming. - traits traits are extremely useful for splitting single responsibilities out of a class. Without them classes tend to handle too much and become big and more difficult to understand. Java 8 default interfaces are somewhat like traits but don't allow instance fields which limits their use in slicing a class up in smaller parts.
- val var def abstraction a
val
can implement thedef
of atrait
. Which is about right; for the calling party the difference doesn't matter. Similarly an implementation can evolve fromval
todef
or reverse without breaking the calling code which is a nice encapsulation.
The following are convenient niceties but not as essential to me:
- for comprehensions
- string interpolation So convenient for logging.
- multi line strings
- lazy vals for late initialization
- call by name using =>
There are a few Scala features that you could use for evil. You'll want to keep a grip on the use of these features in your project by using code reviews and Scalariform CI style guide checks. Twitters Scala style guide Effective Scala is a good starting point on Scala style.
- operator functions Imo there's definitely a benefit in a language allowing you to extends operators. For instance in big data processing you want to do addition on your own data constructs. Or multiplication of matrices. But it's easy to go overboard and create code that's hard to read; if only because other developers won't be able to internally pronounce a function like
~~
. There's a Scala sub-community that enjoys Haskell inspired operators like<*>
. I'm generally not in that camp. - implicits Implicit conversions are discouraged, which is why they're controlled by a compiler flag and feature import. Implicit objects definitely have a role when applied judiciously: they're great for context objects. When programming using threads these are generally mapped to the thread using something like Java
ThreadLocal
. Of course that doesn't work when your processing is asynchronous. An implicit argument to a function makes it clear that your code depends on some contextual data without having to retype the boiler plate argument everywhere in your code. - DSLs are a double edged sword; they can be very expressive and helpful or cryptic and hard to debug. They have that in common with frameworks. In which camp some of the better known Scala DSLs fall is a matter of taste.
All in all I don't want to work with Java anymore if I can help it.
Footnotes
1 the conciseness of case classes becomes crucial when you're using actors on the jvm. Because with actors what would have been a function definition becomes a message class. And having to create a whole java immutable class definition creates just too much friction for something you have to do so often and that is so fine grained.
2 this is how you can do currying in Java 8 to prevent deep nesting in the calling code:
//...
/* koppel de proxiedrequest stream aan de out socket voor de gevraagde session */
Subscription subscription = observable.subscribe(onNext(out), Streaming::onError, Streaming::onComplete);
return subscription;
}
/* this is the only way to extract this function from the nested calling code in Java 8. It amounts to currying.
Action1<JsonNode> is syntactic sugar for Function1<JsonNode,Void><*/
static Action1<JsonNode> onNext(Out<JsonNode> out) {
return (message) -> {
Logger.debug("Incoming message " + message);
try {
String commandName = message.get("name").asText();
//etc