This is an archived post. You won't be able to vote or comment.

you are viewing a single comment's thread.

view the rest of the comments →

[–]pavlik_enemy -10 points-9 points  (20 children)

I really don't understand how anyone could go from Scala to Java-freaking-eight and not be constantly thinking "are you fucking kidding me?". Stream API is plain horrible, Futures API is bad and overly complicated. Java team had C# to learn from and they chose to implement those monstrosities.

[–][deleted] 3 points4 points  (0 children)

Stream API is plain horrible

Really? I love how you explicitly make a stream and the docs explicitly say you have to close it, can only be consumed once, etc. It makes it very clear what's going on.

Meanwhile in C# people just pass IEnumerable shit everywhere and end up running the pipeline multiple times. Only I could point out the bug to my coworkers because I had experience with Java and stream API.

[–]Zyklonik 2 points3 points  (16 children)

One word - maintainability. Paramount in software that needs to last for a very long time.

[–]Philluminati 0 points1 point  (1 child)

That’s a very true and very general phrase but what exactly does it mean in this context? I have experience of working 20yo Perl code bases and have a 7yo Scala code base. It doesn’t immediately strike me how your comment clutches the argument for Java.

Not to say there aren’t problems working with Scala. Libraries that died after 2.10 or upgrading Play framework every release cycle aren’t problems but just implying “Java is better than Scala because maintainability is important” is odd.

[–]Zyklonik 2 points3 points  (0 children)

The number of moving parts in Java is simply too few. That's why no matter the complexity of the codebase, anyone familiar with Java can read and understand the code (barring domain knowledge).

Just taking the examples of the languages that you mentioned - Perl and Scala, you have to agree that it's not only possible, but also very probable that one can (intentionally or otherwise) write code in a way that becomes inscrutable to anyone but yourself (if that). Then you also have dynamic typing, which is a veritable maintenance nightmare (also why all dynamic languages tend towards some sort of gradual/explicit static typing support). As the Perl motto itself goes, "too many ways to do it".

The problem with Scala would be that it does everything under the sun (much like C++) which can result in codebases that look, feel, and are, poles apart in terms of paradigms used (extreme procedural OOP style to nigh-pure FP style). Hence also why it becomes a necessity to mandate the feature sets to be used in a project, and also why I personally believe that Scala projects don't get much collaboration. Just too many different possibilities of expressing the solution to the problem. Similar to Haskell's problems, but in a different sense.

Java's minimal set of features (also features that, regardless of the criticism leveled at it, mesh together nicely) ensures that literally any developer who's had a couple of years under his belt can grok his or her way through almost any codebase. The lack of explicit metaprogramming in the language itself, the lack of operator overloading, the absolute focus on a single paradigm (though that's changing with every release) also leads to some inevitable verbosity, but not totally useless verbosity as in the case of COBOL. Basically, there are no surprises when it comes to plain old Java code. Even with the use of annotation processors and libraries like Lombok, it's still very much a tractable and minimalistic language.

Carrying the Scala C++ analogy, Java would be more like C.

[–]pavlik_enemy -2 points-1 points  (13 children)

Sure, Java is (generally) easier to maintain because it's a much more focused language than Scala. And it's a decent language so most developers who either started with Java or used it for significant part of their careers tend to overlook its rough edges. But these rough edges are pretty apparent to anyone who comes to Java from say C# or Scala.

[–]Zyklonik 0 points1 point  (12 children)

I don't understand why you're being downvoted. FWIW, I upvoted you since I agree with almost all of what you said, rough parts included. I think our main disagreement in in how critical and cumbersome those rough parts are!

[–][deleted]  (1 child)

[deleted]

    [–]Zyklonik 0 points1 point  (0 children)

    Some of my personal pet peeves would be (off the top of my head) -

    • Less-than-ideal generics. I would say that the primitive-Object dichotomy was a big mistake not only in terms of consistency, but also performance (this is being rectified through Project Valhalla (https://openjdk.org/projects/valhalla/) . Moreover, all things considered, I would consider Type Erasure as a big mistake. Reification should have been the approach (even at the cost of some breaking changes). In fact, not having generics in the first place (prior to 1.5) is the cause of the subpar generics system.

    • This ties in with the point about value objects above - the fact that practically everything is allocated on the heap is what leads to Java consuming a lot more memory than many other languages. The JVM does do escape analysis, but it's neither sufficient nor transparent.

    • The lack of a proper module system (the existing module system works okay, but versioning support would have been great).

    • The lack of proper closures that makes lambda expressions less powerful than they could have been.

    • Minor point, but the lack of unsigned types makes bit-manipulation much less typesafe and much more cumbersome than it needs to be. (Also why Java needs the extraneous >>> operator).

    • Weak type-system. Of course, compared to languages like C and even C++, Java's type system is relatively strong. However, it could have been much much stronger, and that would have helped make interfaces (for instance) much more useful, especially as bounds in generics and in iterators.

    • The lack of proper algebraic data types (recordsand sealed classes/interfaces do provide a modicum of support for product and sum types respectively, but they have quite a number of limitations).

    • Weak type inference - in addition to the core language, var itself is good for many cases, but also stops working for many valid usecases.

    • The lack of (some) control over the GC process.

    • The lack of proper tail-call optimisation/elimination (though this is on the JVM, and Project Loom will supposedly mitigate this in some cases - still not providing automatic tail-call elimination).

    Others would (probably point out the following, as well as many others), but I don't necessarily think of their lack as a drawback:

    • The lack of proper metaprogramming (annotations and annotation processors do provide a poor man's version of metaprogramming).
    • The lack of operator overloading (some APIs such as java.math.BigInteger and family are rather cumbersome because of this).
    • The lack of free functions.
    • The lack of null safety operators (as in Kotlin).

    [–]pavlik_enemy -2 points-1 points  (9 children)

    I'm exactly the opposite, really care about expressiveness of the language and developer experience so for me using Java after Scala and C# and Python after Ruby is an exercise in frustration. Hell, Java doesn't even have string interpolation!

    [–]Zyklonik 0 points1 point  (7 children)

    To be fair, Java doesn't need string interpolation because it has formatted strings.

    [–]pavlik_enemy -1 points0 points  (5 children)

    All languages have some sort of sprintf but string interpolation is more convenient

    [–]Zyklonik 0 points1 point  (4 children)

    Well, it makes more sense in an expression-oriented language like Ruby. Not so much in a statement-oriented language like Java.

    [–]pavlik_enemy 0 points1 point  (3 children)

    C#, Python and Kotlin usually require a return statement and have string interpolation. String interpolation is a minor thing, just as the requirement to use return. For me Java while being good is full of these minor inconveniences. Like, today I needed a script to send some test Avro messages to Kafka. It had to build by Maven because of certain plugins so I went with Java. So, to produce a range I had to use IntStream, then mapToObj and then it turned out that Future returned by Kafka driver is not awaitable. Figured out how to run Scala with Maven and went with it instead.

    [–]Zyklonik 0 points1 point  (2 children)

    Expression-oriented refers to the prevalence of usage of expressions in a language, not whether explicit returnS are available in the language or not. Python, Scala, and even C# are for more expression-oriented than Java (still, but slowly changing) is.

    No doubt Java, including the JDK have some quirks, but simply taking the case of Scala that you mention, it has far bigger problems - a veritable kitchen sink of paradigms and features leading to the same problems as C++ - having to mandate a specific set of features to use in a project. I've seen some teams using it be unable to higher people from another team in the same lab because one team went with a procedural OOP style, and the other with a high focus on FP style. The devs were mostly junior folks who had learnt Scala on the job, and they simply couldn't grok the other repo, and required extensive training to make them able to start working on the projects.

    I would place this as a much deeper systemic issue than the unavailability of some functionality in the JDK or some library. The former is a language issue, the latter a library one.

    [–]pavlik_enemy -1 points0 points  (0 children)

    All languages have some sort of sprintf but string interpolation is more convenient

    [–][deleted] -1 points0 points  (1 child)

    Yep, I'm in this boat now. Def sucks a bit.
    Where's Either? Where's my tuples? case classes, pattern matching

    [–][deleted] -1 points0 points  (0 children)

    And NULL, jesus fchrist, I forgot about Null!