Consolidating older blogs: this was originally posted in Oct 2011.
I keep seeing that OMG, Scala is a complex language, maybe it’s true. Let’s chase this complexity for a bit and go through some of the biggest scala differentiators (from Java or C++, as major OO languages).
The compiler can infer the types for the most part, so people have to type a lot less repetitive information, which they used to type in both Java and C++. For instance, since “john” is obviously a String, the type of the variable is inferred by the compiler to be String so I don’t have to type it again:
val aGuy = "John"
I have seen this feature both praised and held against the language as “added complexity”, so I don’t know what to say. I just love typing less and feeling less stupid, every time I declare a value or variable.
Since OO is all about defining classes, scala made do with a bunch of stuff in one go, so that my domain models are dead-simple:
class Person (val firstName:String, val lastName:String)
This, in Java and C++ takes about one page of code: with constructors, getters, setters etc. Scala observed that people don’t need to type one page to inform a stupid compiler that they want a person with a first and last names, so it’s all condensed in this one line, much like a table would look in SQL.
Is this added complexity? Well, I do need to worry about overriding the generated getters/setters ONLY if I need to, so I don’t really know if it’s more complex.
Me? I love this particular feature so much, that honestly, I don’t care what you think J
All programming languages I know discriminate between methods with names like append
and operators like +=
. Some do not even allow re-definition of some hardcoded operators (Java) while some allow infix notation only for operators (C++).
Scala simply makes do with ALL these restrictions, so the name of a method can be pretty much anything and all can use the infix notation, so I can have:
ListBuffer(1,2,3) append 4
// As well as
ListBuffer(1,2,3) += 4
The only difference would be the precedence rules, which are customary in all languages. Some people obviously would see this as “more complex” than both Java and C++ since they can now do whatever they can… but I see it as “simpler” than both. Operators have been held against C++ before so it really is not surprising that they are held against scala as well.
This is, after all, what makes scala such as perfect DSL framework, allowing natural language such as:
"Mary" with "a purse" and "red shoes" should look "nice"
In both Java and C++ (as I last knew that language, long time ago), the generics have certain hard-coded and limited behavior (i.e. co-variance) and allow only a few constructs (like List<T extends Person>
).
In scala, there is a default behavior, where List[Person]
is non-variant, but everything is customizable. If you want co-variance, just tell the compiler List[+Person]
or List[-Person]
for contra-variance. Just like Java, there is List[T <: Person]
and the opposite: List [T >: Person]
.
Since scala supports implicits (with finer control than C++), another construct is available: List[T <% Person]
. There's more there.
Is this more complicated? Well, just like the operators – it lifts certain limitations of other languages, so it’s both more complicated, since there’s more stuff to learn and simpler, since there’s less rules to live by.
I personally enjoy the extra control… do I actually use it? Not on a daily basis, the defaults are good enough.
Most languages only allow data types (objects) to be constructed. This is normal in Java and C++.
Well, there’s the flipside, where I can de-construct an object and I don’t mean de-allocating its memory. Consider this:
someone match {
case Person(first,last) => println (s" name is $first $last")
}
You can see what I mean by de-constructing: took an already created object, someone
, and de-constructed into its components: first and last. I know this looks foreign to most OO personnel, but trust you me, it is insanely cool and useful. Think what you would have to type in either Java or C++ to achieve the same thing, with if (instanceof) and then type cast and assign two variables and whatnot.
Also observe the String interpolation "$first $last"
, introduced since the original post.
Is this more complex? Well, this is totally new functionality so I guess it is. But I love having it! Trust me, you will, too!
By the way, the match/case construct is way more powerful than your regular switch/case which can only handle constants… we can de-construct types, match constants, match types… and more! Check this out:
someone match {
case Person(first,last) => println (" name is " + first + " " + last)
case "John" | "Mary" => println ("hello, " + someone.toString)
case s:String => println ("name is " + s)
case _ => println ("don’t know what this is…")
}
Is this more complex? I don’t know… in Java or C++ this is between one and two pages of code. This looks simpler and more intuitive to me… granted, I got used to it but so can you!
There’s more areas of the language, but these are some of the major differences I have time for right now. If you have others, post up and I’ll get into those as well.
I did not get into the functional areas of the language, since that would require comparing with other functional languages and I’m not an FP guy. C++ comes close by allowing passing pointers to methods to other functions while Java 8 I think has some proposed lambda syntax.
Is it more complex? Well, there’s two ways to look at it:
Yes,
No,
What do I think? I don’t really care. To me it was cool to learn these concepts that I had forgotten since university and my new vocabulary allows me to solve the usual problems in just a few lines of code and head for an early lunch, while my mates are still writing some getter or setter…
There are some negatives with the language... after having used this quite a bit in the last 6 years, this is what I would complain about:
The first is: slow to compile. Not annoyingly slow anymore, but still slower than anything else I know.
The second is implicits - the way they chose to design the implicits and extension mechanism, makes them too pervasive. You could have an implicit from Any to MyCoolThing define in some random scope you import.
The third is the collections library - not a visible issue to most, but still quite a lot more complex than what I'd like to look at.
The fourth is SBT - the Scala Build Tool. It is not simple in any way: as soon as you need to do something out of the ordinary you are screwed: there are so many versions of the non-obvious DSL syntax that google will go berserk trying to tell you how the thing is done. They should have either done it as a completely separate syntax, like JSON or something OR a proper internal DSL, where the compiler/IDE could assist you.
Having said that, the current IDEs (as of 2014) do away with most these problems, AND there's a lot more to like compared to ANY other language I know, including public compiler/interpreter interface and much more...
What do you think?
P.S. This is a great detaiked discussion of complex vs complicated: http://lamp.epfl.ch/~odersky/blogs/isscalacomplex.html