Using Scala Will Make You Less Productive
grundlefleck.github.io
grundlefleck.github.io
In this lengthy [post|comment], I’ll share my thoughts and opinions on [THING IN COMPUTING, TiG hereafter] . . . Using a cavalcade of flawed data; logical fallacies; biased opinion; startling lack of citations; and basic nonsense, I will convince you that using [TiG] will make you less productive. I will finish with an underwhelming and unexciting conclusion. You have been warned.
Think of it! "I'm offering my thoughts here under Ahmdal's Condition, of course . . ." "Well, I think you're starting from Ahmdal's Condition . . ." "That works under Ahmdal's Condition, but assuming stricter conditions . . ."
Honestly, it's genius. If I had a magic meme wand, this guy would be famous by the end of the day.
Ahmdal is not the OP :/
But.
Even first hand anecdotal evidence has its limits. It really needs to be blatant to be significant. Second hand anecdotal evidence is even worse. If the phenomenon you want to catch is subtle, you generally a good deal of reliable data. That generally means something "scientific".
Ah, the dreaded double standard. Easy trap. Personally, I avoid it by trying to have objective criteria. I end up with crude and imperfect measures (LOC count, number of defects…), but at least they're not too biased.
> personal evidence is less strong than scientific evidence.
Actually, this is not necessarily true about first hand personal evidence. While a host of biases are out to cloud your judgement, science can be weak for two reasons: (i) the studies may not amount to much evidence, either because the controlled experiments are also artificial, or because there may not have been much of them (it is quite expensive to do the same project multiple times with arguably similarly skilled teams), or both. (ii) What applies to the majority of people may not apply to you specifically (though you'd better have strong evidence as to why this is the case).
Now, second hand personal evidence… that's much harder, because (ii) doesn't apply, you also have to trust whoever is telling you the story, and this story hold far, far less information than what you would have gathered by living it yourself. Still, it may yet be stronger than sufficiently weak scientific evidence.
Now, a blog post by a random programmer? Okay, that one is always weaker than science.
I can only really offer my experience, which is architecting and building out a 25kloc app and team to go along with. Mostly, because we avoided the 'clever' scala stuff as much as possible, things went well. Maven instead of sbt. Jetty instead of play. The only scala DSL we used was squeryl.
If I was doing it again, I'd probably rethink the use of scala. As helpful as it was, it was also a pain in places. Having to rewrite all our for loops was not cool. Case classes saves on boilerplate, but frankly you only write that stuff once, so it didnt really save us on a lot. Intellij elides most of the junky boilerplate (import statements, generating setters/getters, etc) in Java, so ...
Now a days with the advent of Java8, we have access to lambdas, function pointers/interfaces, and the functional stream system, I am interested to see how far one can get in just pure java. Java is the new scala it seems.
It's actually one of the biggest things I notice when I come back to java land.
I rarely do that any more these days so hopefully it'll become non-issue in the future for you.
So the notion that every single last tiny class has it's own file hasn't ever been true (even if thats the coding style in a lot of code).
You do realize that Scala doesn't have for loops, right?
http://stackoverflow.com/questions/1677419/does-scala-suppor...
On mutually recursive tail calls, yes, the runtime doesn't have support. Because it misses support for TCO, you're ready to call the JVM "godawful"? Might consider that .NET's tailrec support was basically a guideline that didn't work on 64bits .NET up until .NET 4.0 [1] and it's still not optimized, having a rather significant performance hit versus normal function calls.
For the JVM, a prototype for optimized tail recursive calls is in the works and given the attention that invokeDynamic received lately, I have no doubt it will be implemented for JDK 9 [2], especially given that people like Guy Steele are calling for it [3].
Either way, it's not so bad to implement mutually tail recursive calls by means of trampolines in Scala, especially given that you have an expressive type system at your disposal [4].
> you'd be in a pile of trouble if you messed up and your compiler ended up not doing it either
TCO is considered an optimization, not a guarantee, so if you want it to do a TCO by contract, then an annotation makes sense, no?
[1] http://blogs.msdn.com/b/clrcodegeneration/archive/2009/05/11...
[2] http://openjdk.java.net/projects/mlvm/subprojects.html
[3] http://i.imgur.com/5X3VwOy.png
[4] http://blog.richdougherty.com/2009/04/tail-calls-tailrec-and...
Some JVMs such as Avian support this feature, so they may not be godawful. I haven't used them much, so I don't know.
> For the JVM, a prototype for optimized tail recursive calls is in the works and given the attention that invokeDynamic received lately, I have no doubt it will be implemented for JDK 9 [2], especially given that people like Guy Steele are calling for it [3].
This is great to hear, thanks for telling me about it :)
It sounds like I will have a better time in JVM-land next time, if I ever have a reason to go back.
> TCO is considered an optimization, not a guarantee, so if you want it to do a TCO by contract, then an annotation makes sense, no?
In JVM-land, this is the case, which is why I said one should be careful to use the annotation. I don't have a problem with using the annotation. I do have a problem with TCO not being guaranteed in the language spec (like Lua), or at least supported by popular runtime+compiler combinations (like C++).
I would really, really appreciate a Scala without all the crazy, but with all the brilliance. Or, you know, just a community that doesn't go out of its way to overuse all of that crazy.
Just drop the incomprehensible type declarations and the incomprehensible operators. Maybe add dynamic typing, even. Then you'd have a really nice language.
Eventually libraries that will come along that were intended to be used by humans. Sbt will disappear. Flowers will bloom, and the lion will lay with the lamb.
I wrote the same code in Ruby-Celluloid, Python-subprocess, Scala-Akka and Go. When I did this, I came to an strange realization - I absolutely did not have fun writing Scala/Akka code as much as I did writing Go (minus the unused variable fascism).
Again, I'm not at all arguing the ecosystem of the JVM vs Go, but I just had a much better time grokking Go code than Scala/Akka - especially the package manager (pulling deps directly from github, etc.) I even sent in a patch for a go package ("gocmd" on github) barely a couple of hours after I first touched go for the first time in my life.
I dont have production worthy experience in either, so I dont have a strong opinion... but I really ended up wondering if Go makes Scala obsolete.
Does anyone know if I can disable it in the source code somehow and build a Go compiler (I doubt it though - if Go was never built with things like deadcode optimization because of this enforcement)
I dont dispute it is really useful (I worked for 6 years writing C, so I know about traps) but I really, really, really wish they would put a command line option to disable it.
Very very handy...
If you want your unused symbols, you could insert "_ = unusedSymbol" instead.
With scala I feel like I've spent more time waiting for it to compile than actually coding. Especially since we have been doing so much in Python, the switch back to compiled Java (with saccharine scala flavor) is like jumping back a decade in terms of productivity. Also, as you mentioned, the Go syntax is much easier to read, superficially just K&R C style with the types on the opposite side of the variable names.
I know that the IDE support is lacking but this doesn't really bother me since I don't use an IDE with Play apps. I always use a text editor, since I find that IDEs tend to do a bunch of things badly, whereas a text editor and a terminal each do a single thing very well. There's nothing you said about the features of an IDE that I couldn't do just as well with a well-defined find-replace (besides variable extraction - not familiar with variable extraction.)
You can be productive in any language - Java, Scala. But I find that Scala's conciseness - the fact that I can do an operation on a whole collection of case classes using pattern matching and the collection API functions that would take 30 lines using classic Java - ends up making me more productive than I have been in those other languages. Maybe it's just me.
That is: - Type safe re-factorings (why would I give up on my type system to just go grepping around my code base?). To be honest I don't use variable extraction as often in my Scala code as I did in Java, but to have never used it makes me think you are severely undervaluing the whole type safe re-factoring universe. - Automated import statements. - Configurable code formatting.
The IDE space is improving dramatically in Scala, but I do think the Scala teams blessing of Eclipse was a mistake. They should have been writing more generic tools/hooks to empower their users in a wide variety of development setups.
For someone learning Play & scala in general I find the IDE to be immensely helpful in showing my type information and valid auto-completes. Rather than constantly digging through documentation I can see that, for example, some flatMap will give me a list of (Id, User) tuples, so then I can call map( case(id, user) => user) ) (or just map(_._2)) and pull out the user objects to get my list of users from my list of tuples. This lets me focus on the business logic and not spend all my brain power tracking types. I don't think I would have survived my first foray into building complicated composite ResultSet parsers in anorm without the IDE.
I'm sure that as I gain more experience and all the functions become second nature I'll be able to track it in a text editor but for now I love my IDE.
At least, when I was working within play, it was painful enough to discourage me from test driving my code, especially if implicits were involved.
As for integration tests those run via Specs2/Selenium as well but I rarely run them from my own machine as they are continuously running on Travis CI whenever I push to our repositories.
sbt ~test
will run all your tests on file save. sbt ~test-quick
will only run previously failed or new tests on file save.On one Play project we have ~160 test cases, including integration tests that spin up real Neo4j and Elasticsearch instances. Running the whole suite takes around 90 seconds. That would be far less if I did a better job separating unit and integration tests or mocking out the external dependencies.
Except with PHP you hit refresh and it is done. With play you wait 10 seconds and it is done. There is a huge difference.
eh? With incremental builds you should be seeing far more 1 second recompilations than 10 second recompilations.
If you're getting 10 second recompiles, the following help:
1) sub projects, it's an absolute must for non-trivial apps
2) use a 3rd party build system (say, GruntJS) for assets
Also, be sure to ;clean;compile prior to starting an incremental build with ~run (and of course, do this from a play> prompt and not bash, you want a warm JVM)
Scala is not, in my experience, a hit the ground running type language. You have to learn the ropes, discover the various gotchas (of which there are many), etc. before you begin to blow away your former self in terms of productivity.
* Compile-time is slow * IDE support is more poor than Java * Overuse of operator overloading is confusing * Implicit confusing
I don't necessarily agree with all of these criticisms, but it's a very valid piece of "feedback" to the Scala community.
If you give your methods bad names, it really is not the language's fault. You can probably find a way to make a bad API even if you are limited to a-zA-Z0-9 ...
If a library has an inscrutable set of method names blame the lib not the language.
The special operator rules allow library authors to create a "DSL" without using the (difficult) macro system. This almost never ends well.
Odersky has taken an official stance against symbolic method names, especially in libraries, and many of the most famous offenders (Dispatch's absurd periodic table) are rightly ridiculed within the Scala community.
I don't have anything to add on implicits. They've certainly tripped me up a few times but generally it's just a matter of finding the right import, though I have been cursing Play's implicit JsValueWrapper the last few days.
I have confidence to advise building real businesses with Scala stack and not looking like an irresponsible guy who just wants to play with the latest and shiniest stuff hazarding a company's business. I also see how Typesafe is focusing on solving bad parts: compiling time, stability, etc.
You can also stay away from advanced / theoretical / crazy parts of Scala most of time. You don't need to use Scalaz, advanced type system features etc. There are also immature libraries, toys and proof-on-concepts, but you cannot blame Scala for your choice.
On macrolevel, the obvious thing is that you can separate your projects. I also have some expectations for upcoming Scala release.
But that's a really good question to ask anyone from Twitter and Co.
The parts that make it worth using in the first place?
>You don't need to use Scalaz, advanced type system features etc
Might as well stick with java then. You're not getting the benefits of scala vs java, but you are still paying the costs like compile times and poor tool support.
Sticking with Java you lose both the expressive Scala features and some good frameworks without Java burden.
Btw under "advanced parts" I didn't mean "everything above Java". I enjoy immutability, collections, case classes, pattern matching, first class functions, lambdas, terse syntax not considered them "advanced parts". Advanced parts are all those lenses, recursive types, type lambdas, you know.
What it does wrong is when copy/pasting code or when moving a class/trait to another file/package, it sometimes misses the imports for the implicits that the code is using. You can't really blame it, since an imported implicit is not hard-wired in the same way an explicit reference or type usage is. Depending on your perspective, this may be a feature and not a bug :-)
Either way, IntelliJ IDEA gives awesome in-editor error highlighting and you also have Scala's compiler to double-check things for you, so you really don't have to double-check the IDE's work, except in cases where there are problems.
> The Java-to-Scala tool emitted code that would never have compiled on any version of the Scala compiler.
It's pretty hard to translate from Java to Scala and you're right that it doesn't work. On the other hand, idiomatic Java code is pretty freaking far from idiomatic Scala code and I think you should either leave that Java code as is (mixed projects are easy to configure) or you should translate it in idiomatic Scala code by hand, a task that no transpiler could do.
This does sound like a feature and not a bug, yes.
> It's pretty hard to translate from Java to Scala and you're right that it doesn't work. On the other hand, idiomatic Java code is pretty freaking far from idiomatic Scala code and I think you should either leave that Java code as is (mixed projects are easy to configure) or you should translate it in idiomatic Scala code by hand, a task that no transpiler could do.
I agree, but some of the team used IntelliJ's make and apparently our particular project configuration had trouble with Java in some projects, so where there wasn't much Java the solution we went with was to move it to Scala. There were other more reasonable solutions available like "Fix the config" or "Just use xsbt bro", but it wasn't my call.
Which I already conceded as being in Java 8. Not sure what your point is.
Tool support is outstanding with IntelliJ
The only similarity that Lisp and Scala have are that they are both functional programming languages[0].
Scala is a heteroiconic language. At least until recently, Scala didn't even have macros (I think they may have added that recently).
Scala is a nice language, but saying that Scala is somehow like Lisp is doing a disservice to both languages.
[0] Even the statement that Scala is functional is itself debatable - one can make the argument that it is "just" more purely object-oriented than Java, but that's a whole separate discussion.
Maybe we're just defining our terms differently, but the closest Lisp comes to a functional programming language is Clojure and before that Scheme.
This is not meant to deride Lisp in any way (Clojure is my language of choice, after all) - I think all Lisps enable a fundamentally unique way to reason about programs, but that's a characteristic of Lisp and not of functional programming languages.
(if a x y)
which is composable to (let ((val (if a x y)))
...)
as opposed to (let ((val nil))
(if a
(setf val x)
(setf val y))
...)
That said, not everything returns value(s), and there's nothing enforcing immutability. I'd say you can be functional in (most?) lisps if you are careful, but the language family is more geared towards general purpose programming than functional.More recently, people have confused "functional" with other features of languages like Haskell (sometimes called "purely functional"), leading to some confusion about what defines functional programming.
Here's an interesting post about the more recent changes in the phrase's meaning:
http://wcook.blogspot.co.uk/2012/07/day-functional-programmi...
I checked a few common definitions of homoiconicity and it seems like Scala is within the ruleset.
Isn't quoting necessary to "reify"/refer to the actual code as data?
This is a fallacy that gets repeated over and over, ad-nauseam. No, you can't do functional programming in any language. In Java it's more than just lack of syntactic sugar. Java's type system actively fights against functional programming. This is a bit like saying that you can do OOP in any language. Yes, you can have GObject in C. Yes, for Java there have been attempts like www.functionaljava.org ; but it sucks so badly that almost nobody does it, unless they are forced to do it.
In fact I challenge the author of this article to show me a piece of FP code that he wrote in Java by himself.
> What about all the wonderful things in the standard library? Yep, filter and map and fold operations on collections out of the box is nice. That the SDK uses Option instead of null is an improvement. These things are nice, but I generally get what I’m looking for by adding some libraries to core Java.
I do know Java's ecosystem and he must be speaking about Guava, as there's no other stable library for Java that has persistent data-structures. And Guava kind of sucks, so if you're looking for persistent data-structures, might as well implement them yourself, it's fun and it might open your eyes to what functional programming is ;-)
Speaking of Guava's Option, lets talk about how Guava's Option is not a Monad, or for those that don't know what that is, basically a context that implements map and flatMap with certain properties. Scala's Option also implements filter and fold, etc... which in regards to the purpose of a Monad, in the words of Erik Meijer, is to guide you through the happy path. Now that's a little closer to functional programming and speaking of Erik Meijer, people might be interested in what he thinks about Scala: https://twitter.com/headinthebox/status/438355100310831104
On the bad things he lists:
> Compile Time
Scala's type-system is much more static, leaving much less room for accidental bugs. Scala's compiler does things that in other languages you'd need to run specialized tools to get the same results or to write more tests. A single sentence or paragraph can't do it justice, but the big compile times are perfectly justified and this is coming from a developer that prior to Scala preferred dynamic languages with instant edit/reload cycles.
Besides, these times have been dropping as the core Scala devs have been making improvements, with another batch of optimizations coming in Scala 2.11. And incremental compilation in general works great. And I'm a developer, I have a good dev machine - if you don't have an SSD and at least 8 GB of RAM, then you've got no excuse.
> The IDEs
I've been using IntelliJ IDEA since a year ago and for people reading the rant exposed by the article - IntelliJ IDEA 13 for Scala is everything you'd expect an IDE to be and more. I never had problems with renaming things. I never had problems with finding all usages of a type. IntelliJ IDEA 13 works flawlessly for viewing the inheritance hierarchy and I'm doing the Cake Pattern which is freakishly heavy on the traits ;-) Other languages only dream about such awesome IDE support.
> Overuse of Operator Overloading
He complains about SBT, but I thought we were talking about the language and the standard library. Pity that he doesn't exemplify an instance in which "+" is overused.
> Implicit
He mentions implicit parameters, but exemplifies with implicit conversions. Implicit conversions are in fact considered experimental and starting with Scala 2.10 the compiler will issue warnings in case you're using them without allowing "language.implicitConversions", being replaced with extension methods by means of implicit classes.
Yet, he doesn't mention Type Classes, the main use-case for implicit parameters. IMHO, I can't stand languages that do not have this form of ad-hoc polymorphism.
> How much of your time, as a software developer, do you spend doing stuff that is language agnostic?
That's setting up a straw-man. The problem we as software developers are trying to solve when picking new languages is one of gaining access to abstractions that are otherwise very difficult to express and use. Implement something like Iteratees in Java, a higher-level abstraction for safe asynchronous stream processing with back-pressure built in and you'll see what I mean.
That is a bogus excuse. Languages with better type systems have faster compilers: haskell, clean, F# and especially ocaml. Paul Philips makes a pretty convincing argument that scalac is slow because it is a big mess.
F#'s type system doesn't do higher kinded types, or type classes, amongst other being much less sophisticated.
I'm really tired of hearing the Paul Philips argument. Scala 2.11 is a big refactoring addressing many of his complaints. Will that stop people from using the Paul Philips argument in conversations? Of course not, as many people weren't interested in his actual arguments.
Umm, no it isn't. I'm not sure I'm aware of a faster compiler. It's so fast most people don't even realise that they are running the byte code version (ocamlc) rather than the native code verison (ocamlc.opt).
I never dismissed Paul Philips' arguments. In case you missed my not-so-subtle critique, I'm complaining about people referring to his presentation as a tool for bashing Scala, without having listened to those arguments. What's a "mess" anyway? Scala-js.org happened in a fantastic time-frame because it reuses Scala's reference compiler. How many languages can claim such performance?
People are also working on fixing many of the problems raised by Paul Philips, with Scala 2.11 being a result of that work. And oh look, Paul Philips is still on the contributors list, amongst many others: http://scala-lang.org/news/2014/03/20/release-notes-2.11.0-R...
You're doing both an appeal to authority for an otherwise unconstructive criticism (yours, not Paul's) and an ad-hominen in absence of actual arguments. Also for a random fanboy, at least I'm putting a real name behind my 2016 days old account ;-)
So can scalac. Many compilers have pathological performance cases. Just repeating "the sky is green" over and over won't make it so. It is well known and widely accepted that scalc is slower than ghc. You don't have to like it, or even accept it. But responding with nonsense strawman arguments as if I am bashing scala is pathetic even by hn standards. You made false claims, I pointed out that they are false. Quite simple.
>People are also working on fixing many of the problems raised by Paul Philips
Of course they are. Why do you think "people are fixing those problems" is evidence to support your claim that those problems don't exist?
He doesn't think the language is perfect (no one does) but he doesn't think any other existing language meets his standards either.
A more non-conventional Java (but very far from crazy) is to use property accessors (person.name) instead of methods (person.getName()) for "data objects" (structs, really). Some people are very much against it as they believe it stops you being able to modify your object later, but I'd say modifying the access behavior of a struct (eg, O(1) to O(n) access) is even worse than forcing users to update their use of the object.
Anyway it's a far deeper topic than just "crazy java".
Yeah, it's a bit of a fallacy that requiring getters and setters (especially as simple pass-thrus) for simple properties is required for "good" OO (particularly that it maintains encapsulation). The bottom line is that providing both getters and setters makes the property mutable just as it would be via direct property access. Except now there's a performance penalty, as you mentioned.
And renaming a property typically means renaming the getters and setters anyway lest the property name no longer makes sense internally. Any good IDE will allow you to accommodate either refactoring (property name or accessor method names) with ease.
Initially, setters were also intended to allow the object to fire an event in the case that it's Observable or otherwise participating in a listener-style event system, such as that prescribed by the JavaBeans spec. But, in practice, that pattern is followed so rarely that it hardly merits forcing all of that baggage on every value object ever written.
class Person {
public int age;
}
Now a 'best practice' is to create getters and setters: class Person {
private int age;
public void setAge(int age) { this.age = age; }
public int getAge() { return this.age; }
}
This obviously makes the code uglier. This is done because maybe in the future you'd want to change the implementation of how age is stored inside the object. However, I think this is exactly where nasty performance bugs can slip in. Accessing 'age' or 'getAge()' are currently both O(1) operations with 'age' being slightly faster. However, 'age' will always be O(1) as guaranteed by the language, so using it inside a tight loop is fine. However, what happens if we modified our getAge() to be O(n) because we wanted to store age differently? eg: class Person {
private List<Integer> subAges = ...;
public void setAge(int age) { ... }
public int getAge() {
int age = 0;
for (int subAge : subAges) {
age += subAge;
}
return age;
}
}
What we've done here is utilize our ability to change our getAge method without impacting anybody who is using our class. However, we've turned our getAge from O(1) to O(n) with absolutely no warning to anybody using our function. We've caused far more harm than simply requiring other developers to update their code - we've sabotaged the performance of all the users of all the software that uses our library.So imo the 'benefit' you get from getters/setters is more of a danger than anything. If the implementation of a library changes enough that you need to change your data access objects, you should just release a completely new API anyway as all the assumptions made by users of your library are now right out the window.
So much experience! Scala isn't for the beginners.
> I’ve given irrefutable scientific evidence on why Scala will make you less productive.
Yeah, science! Those are just your opinions not scientific evidences. You're just don't know how to program in other languages(only in simple languages as java, javascript and python).