Will Java 8 Kill Scala?
This is an expansion of my HN comment In response to a comment by HN user justinsb on HN about this article of the same title
The original comment (by HN user justinsb): #
It isn’t that Java 8 will kill Scala, it is that Scala has demonstrated the utility of lambdas to the point where they can safely be incorporated back into Java. So Scala has “won”, but in my opinion likely Scala will now fade away. Java is necessarily very conservative with adopting language features, because it is very careful to maintain backwards compatibility. Java is a bit like Debian stable - out of date compared to Debian testing or even other distros - but solid as a rock. For features to make it into Java, they really need to be proved first in other languages or in external libraries.
This isn’t the first time Odersky has advanced Java either - he worked on Pizza, which showed how to add Generics to Java. Scala demonstrates how to add functional features to Java. Generics were a huge advance, and I think lambdas will be as consequential.
So if Scala does die, it will be as a sacrificial lamb, rather than as a casualty of an unseen war.
My reply (enhanced with examples) #
As a Scala and Java developer, I am not even slightly tempted to replace Scala as my main language for my next project with Java 8.
If I’m forced to write Java, it might better be Java 8, but if I have a choice, there are so many things (as the OP correctly states) that make Scala compelling for me beyond Lambdas that just adding that feature to Java doesn’t really mean anything to me. Ruby has Lambdas, so does Python and JavaScript, Dart and I’m sure any other modern language. I like Scala because of so many other things other than lambdas that a single comment is not enough.
But to name a few (some were referenced by the OP)
- Everything is an expression and can be assigned
val x = try { ... } catch { ... }
val y = if(...) else ...
val z = for(...) yield {...}
- For comprehensions (especially with multiple futures, resolving the callback triangle of death in a beautiful syntax IMHO)
//pseudo REST API... don't be alarmed
val result = for {
//returns a future
response <- request("some/rest/api")
//just a regular variable
someField = response.toJSON.find("some.property")
if someField == "someExpectedValue"
request2Body = s"{someField: $someField}"
//returns a future
response2 <- request("other/rest/api", request2Body)
//and so on
} yield {
//do something with response2
}
result match {
case Success(actualResult) => //do something with the result
case Failure(x) => //x is any throwable thrown along the way
}
- Implicit conversions
//"add" a new method to java.io.File
implicit class RichFile(val file: File) extends AnyVal {
def unzip(newFolder: File) = ZipUtil.unpack(file, newFolder)
...
}
- Case classes
//One line in scala
case class Person(name:String, age:Int)
//is doing the same as 61 lines in Java
public class Person {
private String name;
private Integer age;
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
public static Person apply(String name, Integer age) {
return new Person(name, age);
}
public void copy(Person p) {
setName(p.getName());
setAge(p.getAge());
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
if (age != null ? !age.equals(person.age) : person.age != null) return false;
if (name != null ? !name.equals(person.name) : person.name != null) return false;
return true;
}
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + (age != null ? age.hashCode() : 0);
return result;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("Person{");
sb.append("name='").append(name).append('\'');
sb.append(", age=").append(age);
sb.append('}');
return sb.toString();
}
}
- Pattern Matching
def eval(expression : Expression, xValue : Int) : Int = expression match {
case X() => xValue
case Const(cst) => cst
case Add(left, right) => eval(left, xValue) + eval(right, xValue)
case Mult(left, right) => eval(left, xValue) * eval(right, xValue)
case Neg(expr) => - eval(expr, xValue)
}
- Tuples
val pair = ("apples", 3)
val (fruit, num) = pair
- The fact that everything has equals and hashcode already correctly implemented (so I can put a tuple, or even an Array as a key in a map)
val cityPopulation = Map(
("US", "GA", "Rome") -> 36303,
("Italy", "Rome") -> 2753000
)
cityPopulation.get(("Italy", "Rome"))
cityPopulation.get(("US", "GA", "Rome"))
- String interpolation
val x = "variables"
s"I can embed $x in code!"
- Multiline string
val justPaste = """
put here
anything you want
including " or '
"""
- Default parameters
def foo(x:String = "", y:Int = 0, z:Boolean = true) = ...
- Named parameters
foo(z = false, y = 3)
And many more:
- Built in dependency injection (see cake pattern)
- Built in “Singleton” pattern with
object
- Lazy variables (not so easy to implement in Java…)
- Most complex yet most powerful type system in any language I know of
- Type inference (not as good as Haskell, but better than the non existent in Java). The fact I always get the right type returned from a set of “monadic” actions thanks to infamous things like CanBuildFrom (which are pure genius)
- Let’s not forget pass by name arguments and the ability to construct a DSL
- Much better and more elegant futures / promises
- Macros!
Yes, compile times are worse, but I can live with it, and binary compatibility is still a big issue, but somehow it doesn’t amount to causing me to ditch Scala in favor of anything else at this time.
I think Scala is here to stay, at least for Scala developers, I am 100% sure you will not find a single Scala developer that will say: “Java 8 got lambdas? great, goodbye Scala forever!”.
The only reason I can think of is compile time and binary compatibility. If we ignore these two, all I can say is that this just proves how Scala is in the right direction (since Java 8 lambdas and default interface methods and steams are so clearly influenced)
I do wish however that Scala will improve Java 8 interoperability, e.g. support functional interfaces the same way and add new implicit conversions to Java 8 collections as well as take advantage to improvements in the JVM.
I will replace Scala as soon as I find a language that gives me what Scala does and does it better. So far I didn’t find such a language (examined Haskell, Clojure, Go, Kotlin, Ceylon, Dart, TypeScript, Rust, Julia, D and Nimrod, Ruby Python, JavaScript and C#, some of them were very promising but since I need a JVM language, and preferably a statically typed one, it narrowed down the choices pretty quickly)
Java 8 is even close, sorry. Great improvement, I’m very happy for Java developers that will get “permission” to use it (might be easier to adopt than Scala in an enterprise) but this is not a reason for a Scala shop to consider moving back to Java.