These Modern Programming Languages Will Make You Suffer
suzdalnitski.medium.com
suzdalnitski.medium.com
It is fine - or at least only somewhat stupid - to have an arbitrary ranking of programming languages, though things like "no garbage collection is bad" is obviously domain-dependent and the author should be upfront about that. But it's extremely frustrating when people pin objective-seeming criteria on their personal preference with seemingly no hint of irony or self-awareness.
Maybe we should all be using ReasonML instead of Typescript, or Elixir instead of Python, or F# instead of C++. Now explain why your CPU is 1,000 times more likely to point at code written in the latter than the former. If your answer is "oh, well I'm just much smarter than the people making these decisions", then you're probably missing something.
'suffer', 'trillion dollar mistake' - cut the hyperbole.
Take the debate about null in the typesystem: Yes, having some information at write/compile time about whether some function may return something in the vein of 'not found' or 'not set' is quite useful. However, `null` as a reference, even though that is exactly what is usually 'said' (the null reference was a billion dollar mistake!), is something different. Typetag based typing systems can have the null reference and nevertheless provide full write-time information about it: "This variable is of a type that isnt tagged 'definitely not null' and you are passing it as an argument in a position where you are required to have this tag'".
It's language design; things are complicated. Pithy maxims such as 'suffer', 'terrible', 'biggest crime since MS-DOS', and astronomic dollar amounts aren't helping.
Can we try to put an end to this hyperbole? This very article shows where it goes: We went from 'billion dollar' mistake to a 'trillion dollar mistake' in the span of 3 paragraphs.
I'm not looking forward to reading a hackernews post about the Vigintillion Euro Mistake in the near future to describe some solution to a complicated aspect of programming that somebody disliked a little bit.
Is this meant to be some self-referential joke? There are dozens of paragraphs between these two, and in both cases they refer to something else.
Also, .NET 5 (well, C# 9) brings record types, a.k.a. immutable data structures.
The C# complaints boils down to nulls, exceptions, being object-oriented, and not being functional (the latter two of which is one issue broken into two parts, in order to better ding OOP languages and promote functional ones).
1. Rich Hickey is quoted in the article, Clojure is mentioned in two other instances, yet Clojure itself is never subjected to the author's evaluation. That seems strange.
2. The quibble about NaN === NaN being false in JavaScript. That's what every language should be doing since that behavior explicitly defined in the IEEE 754 standard. NaN === NaN should always return false and NaN != NaN should always return true. It's in the standard.
3. Dynamic typing is bad. There's pros and cons to dynamic typing. Saying dynamic typing is bad for all circumstances simply reveals a lack of professional maturity and subtlety when selecting tools. Yes, there are many scenarios where the benefits of dynamic typing are outweighed by its cons, but likewise there are many scenarios where the benefits of dynamic typing far outweigh its cons. Literally, YMMV depending upon the problem you're trying to solve.
4. Lack of garbage collection is bad. Like dynamic typing this is not a universal. There are times when you do not want garbage collection. Most of us aren't working in such domains and so yeah, we want garbage collection. The author fails to recognize the existence of domains where garbage collection is harmful to the runtime environment.
Bottom line? It's good to have choice. We're not all programming in the same domains and working on the same challenges. The trade offs we're going to make based off the pros/cons of each feature are going to be different - and that's a good thing!
As for me, I've been programming for nearly 40 years now and my go-to modern languages are JavaScript, Python, Go and Rust. They're modern, have plenty of support and libraries, lots of people know them and want to use them, AND they keep this old-timer's CV up-to-date! I learned long ago the key to success is to be able to get stuff done, not learn a bunch of languages and don't worry about what anyone else thinks is cool.
Ridiculous. `Rc` exists for one purpose: reference counted data. When all the references are gone, it will be freed. That’s garbage collection by definition.
I already knew this article had dubious judgment when they knocked C++, the daily driver for performance-obsessed game developers, down because of lack of concurrency support though.
My favorite error is also rust related :
> Rust has no built-in support for immutable data structures.
This piece is flamebait.
It depends on your definition of garbage collection though, doesn’t it? People often make a distinction between reference counting and garbage collection. Even though you can use reference counting in a GC'ed system (example: CPython), in most conversations people talk about reference counting as something different from garbage collection—for example, Objective-C’s ARC is seen as a different memory management technique from GC (you can have one or the other).
I think most people would not describe Rc or std::shared_pointer to be garbage collection.
Gonna stop you right there, because you’re arguing about which type of memory management is BETTER. That conversation is not one that’s going to come to a conclusion on this website because outside of certain scenarios where you might be forced to choose reference counting or garbage collection, in general, it’s just a tradeoff.
Reference counting is generally slower and less accurate, garbage collection uses more memory and has poorer latency guarantees. ¯\_(ツ)_/¯
> If you’re finding that as a fault of Java, you’ve entirely missed the idea of the language.
I don’t understand what you mean by that. Java is a product of the 90s and “everything is a class, classes encapsulate mutable state, subtype polymorphism” fell out of that. It was a bit of a fad, and the fad got baked into the language.
> It was a bit of a fad, and the fad got baked into the language
That may be its origin story, but if you look at Java among its current contemporaries, the story is, "What would happen if we had a language where literally everything had to use object-oriented programming, including Hello World? How would it turn out?" and the answer is very popular and used in enterprises heavily. If you can't stand OOP, the notion of even entertaining Java as a viable language is a flawed premise.
That’s a good question! The answer is pretty interesting. It turns out that there are a number of factors. Speaking in generalities here: reference counting touches pages precisely when you are done using them (thwarting possibly multiple LRU caches), and the deallocation must run interleaved with the mutator, which is a classic slowdown. Garbage collection algorithms like mark/sweep or copying collectors can avoid writing to dead objects, can put any state needed for garbage collection in a contiguous space, and can run “all at once” so the deallocator is not constantly competing with the mutator for cache, etc.
And then there’s the fact that with reference counting you still have to increment / decrement the reference count, and even if you only incref / decref each object once, there are still faster garbage collectors out there (measured by throughput / amortized costs).
This is the explanation for the empirical observation that reference counting is generally not very fast, although there are cases where it works well.
To get back to the original point—the claim that “Rust does use garbage collection” should not be controversial. The fact that you could beat Rust into submission until a garbage collector comes out is not really a counterargument I care to entertain.
> Rust (like all languages) has the capability to build a mark-and-sweep collector if you need it, …
You would have to build a fairly primitive mark-and-sweep collector, or rely heavily on unsafe constructs. You wouldn’t be able to compete with, say, Go or Java, which insert instructions into the generated code to make concurrent garbage collection work (synchronization points).
You would probably be able to compete with implementations like CPython, which has stable pointers, reference counting, and uses mark-and-sweep to collect cycles.
> You can't accidentally get a GC pause in Rust, which is a feature.
It comes with tradeoffs. For example, Rust lets you leak memory, and allocation is slower than in GCed languages.
> …and the answer is very popular and used in enterprises heavily.
That’s selling Java short. There are a lot of reasons why Java is popular, and explaining it in terms of OOP really doesn’t do it justice.
You can use arena allocation when these issues are relevant (which is not that often). Generally speakingm while obligate RC (particularly ARC) is indeed lower-throughput than obligate tracing GC, Rust does not really use obligate RC.
Other than that, this article is very clearly a FP vs non-FP article. Listen, I think FP is great too and I love a lot of concepts that come from it, but there are some drawbacks when it comes to readability and maintainability, at least for an average Joe like me and most of your peers. I've had to write quite a bit of SML for class this past semester, and will be writing quite a bit more next semester. I've never written more concise code in my life, but those 17 lines for that function take a lot more thought to write for something that isn't naturally recursive. Again, I'm just your average full time imperative programmer, so I may be missing the light.
I just think that most of the best concepts from FP have been making their way into imperative languages, and I think it'll be like that for a long time. Rust's sum types are killer, LINQ and modern iterator systems, and first class functions are all things I find essential in any modern language I'm going to work in, and I have FP to thank. I just don't think that raw FP is the golden ticket for the average programmer.
> Catching exceptions is a bad way to handle errors.
I don't agree. If used properly exceptions are a very elegant way to handle extraordinary conditions and events. Of course we have to ignore the horrible mess Java created with their checked exceptions.
I realized the power of exceptions while I was reading the book UNIX Network Programming by Richard Stevents. I was stuned to see that in some examples most of the code was not implementing the logic, but handling errors. Exceptions would make such implementations very consise and easy to read and comprehend.
> Nowadays there are much better mechanisms of error handling, possible errors should be type-checked at compile-time. Languages that do not use exceptions by default will be ranked higher.
What are they? I don't like the golang aproach each function to return 2 values (result and error code) if this is what the author means.
If anything the golang approach is creating even more boilerplate, thing the author complained about more than once
f, err := os.Open("some_file.ext")
if err != nil {
log.Fatal("Oh noes")
}
x10000 across the codebaseThen somewhere in the middle the article just takes a major plot twist and takes on functional languages as being the solution to everything. While I personally like F#, I find it really confusing that it still gets the "like" for its "ecosystem", although the article then proceeds to explain it's "rather small".
Might as well say problem solving is bad for developer productivity.
This article is yet another example of why experience does not equal expertise.
Also the guy is showing his lack of experience with a bunch of these languages, and doesn't talk about build speed or how IDEs & indexers handle large code bases or how well build speed scales. It's an infamous problem with haskell for example and he doesn't mention it once.
They say Go is the best for "systems programming" but they give it 2.5/5
They don't explain why any of their high rated languages can't be used instead of Go.
Their conclusions are:
Frontend: ReasonML
API Development: Elixir
But if someone were looking to upskill for a job in Front end / Back end web development, are these really the first languages they should be recommended to look at?> Elixir/ReasonML/React
As the author says himself, languages are tools, it's up to you to choose the right one for the job
Unfortunately I think the author failed here. This is extremely objective and extremely biased. But I don't think it's the author's fault, comparing languages is always going to be subjective.
I'm guessing you meant to say "This is extremely subjective and extremely biased". Am I correct?
The null value is a necessary and critically important value: sometimes you have to represent 'nothing.' It's not particularly special as a value. It doesn't break type systems that are designed for it.
The issue seems to be that some people forget that their code might receive null values and that nulls are represented as a zero pointer which cause an exception if dereferenced.
The resulting backlash is worse than the disease: straightjacketing languages and littering them with more busywork.
I've gone the opposite direction in my work: not only can pointers be null but so can all other basic types. It's incredibly useful and a real design win.
> sometimes you have to represent 'nothing.'
Languages in which all values can /also/ be null break the "sometimes" requirement there. Not all variables/parameters should be able to represent nothing. Representing nothing isn't the issue here, I doubt anyone disagrees that that's useful (there are still Options). The fact that anything can be nothing is.
In my experience nullability is a dynamic constraint and it's most often just passed through various functions, until it becomes an issue some function. Static null checking then just becomes madness.
Other representable values can also can be invalid inputs to some code, but we don't implement draconian constraints throughout the code to manage that.
What about the Lisp (Common Lisp, Scheme, Emacs Lisp, Clojure, etc) and Pascal (Pascal, Delphi, Ada, Oberon, etc) families?
He somehow believes the latter is easier to learn. That’s hard to square with their relative popularity in the front-end world.
Also, Linus Torvalds opinion about C++ is only relevant to Linux (kernel).
The text is just trolling.
C++
> Garbage collection was never added into C++. Manual memory management is extremely error prone.
C++ has memory management simplification: reference couting via shared_ptr/unique_ptr. The trade-offs are different, but in C++ manually managing memory is not necessary unless you have a good reason.
> C++ is notorious for its slow compilation time. Significantly slower than Java, not as bad as Scala.
That depends on what you use and how you use it. However, this is tricky to figure out what slows down the compilation.
> In C++, all references are nullable.
Looks like this guy really needs to read up on C++. References can point to null, but not so easily. I think he means pointers.
> Catching/throwing errors is the preferred error handling mechanism.
I think he should really watch Herb Sutters Video[see 1] on the improvements of exception handling that are in the pipeline. Herb also discusses the alternatives to exception handling.
C#
> Multi-paradigm?
C# has Pattern matching since 8.0 [see 2]
Python
> Python is dynamically typed, there’s not much more to say about the type system.
Dynamically but strongly typed one could say.
> In comparison, NPM in JavaScript is the only tool you’ll ever need.
JS has Yarn as well...
> I don’t recommend using Python for large projects, the language was not built with serious software engineering in mind.
no arguments provided... and in fact there are very large python projects out there in the wild.
Also, pytest is one of the best test frameworks that i have seen.
Rust
> Rust was designed from the ground up to be fast. Compilation of Rust programs takes longer than compilation of Go programs.
i don't understand why he bashes C++ for slow compilation times and not rust. According to [see 3] rust is twice as slow on some benchmarks...
> Rust is the only modern language on our list with no garbage collection. > This forces the developers to think about low-level memory management, and makes developer productivity suffer.
No mention of the borrow checker etc, I find extremely disappointing.
> Rust has no built-in support for immutable data structures. Discussions like [4] should be referenced - i think when discussing this topic.
JS
> JavaScript has good libraries for working with immutable data (like Rambda/Immutable.js).
Weird that he doesn't mention the library efforts in C++ [5] and Rust [6] and Python for immutable data-structures [7][8]
----
[1] CppCon 2019: Herb Sutter “De-fragmenting C++: Making Exceptions and RTTI More Affordable and Usable” https://www.youtube.com/watch?v=ARYP83yNAWk
[2] https://docs.microsoft.com/en-us/archive/msdn-magazine/2019/...
[3] https://users.rust-lang.org/t/are-there-any-compilation-time...
[4] https://users.rust-lang.org/t/persistent-data-structure-supp...
[5] https://github.com/arximboldi/immer
[6] https://github.com/orium/rpds
Oh boy here we go.
Huh, I have never heard of it.
Functional languages are better than OO languages.
C++ is the worst language
Elixer is the best language.
The very idea of comparing C++, Elm and Elixir is silly in itself. What a joke.