Thank you for your post!
Having programmed in all these languages myself, I find myself disagreeing with you on a couple of points:
First, let me start off with mentioning that every language has its warts. Being a polyglot programmer is, in that regard, both a blessing and a curse: I miss all the features the other languages have, while being slightly annoyed at times at the odd implementation-details or things that exist because ‘it seemed like a good idea at a time’ (and are kept for backwards-compatibility).
I love all the languages (truth be told, some more than others, but it is definitely only a partial order):
- About the complexity of the abstractions in Haskell. Yes, you are right, Haskell has mechanisms that can be very difficult to understand. However, I have the strong feeling that this is caused more by a lack of good documentation and tutorials than because ‘abstraction is bad’. Be aware that complexity is not the same as difficulty; often, Haskell turns out to be harder for developers whose mind already contains the leaky abstractions that are present in other languages: For instance, working with recursion is only more difficult than working with loops if you already know loops.
And I have to say: In the last four years, the amount of beginner and beginner-intermediate material about Haskell (and pure functional programming in general) has skyrocketed; probably in part caused by more and more people from other communities looking at the functional paradigm for inspiration.
Haskell’s type system does not exist ‘because it is nice’ (although, of course, tail recursion is its own reward ), but because it allows you to write code that is as decoupled as can be: For instance, you can write algorithms that work on certain kinds of containers (like: ‘all things that you can add a new element to’) without knowing:
a) what type of actual container it is, nor
b) what things are stored inside.
And because this not only works on containers, but because computational statements are also a ‘container type’, it is possible to leverage the compiler to do insane things, like running a piece of code no longer sequential, but as parallelized as is possible.
That said, Haskell definitely has some rough edges. Most of them are caused by its age (because now we know more than we knew back then), resulting in:
- A Prelude with some non-total functions, which basically go against how to program properly in the language.
- Some things have very counter-intuitive names, especially when coming from a different programming language (
class
is for instance used to create a Haskell typeclass. If it had either been called interface
or protocol
, then a lot more people would have understood it the first time).
Ok, besides my first and maybe most important point, that it is mostly a difficulty to get started for new programmers because of a lack of good tutorials (which seems to be changing), let’s attempt to answer some of your points directly:
Lenses exist because they allow you to decouple what you want to read/change from how it is stored, meaning that you do not have to hard-code your accessors (which in many other languages, you are forced to). In Elixir, we basically have lenses too: It’s called the Access
behaviour.
To be honest, I have never found the need to be mindful about the implementation details of Haskell while writing my code, (other than a general sense that Haskell is non-strict in its evaluation, which is not really an implementation detail). The only times you’d dive into this, is when you want to get that last 20% of speed or memory-optimization, which you almost never need. (The root of all evil, etc.)
Ruby is a language that is easy, but definitely not simple: The objects (the proverbial ground) you are holding are shifting under your feet all the time. It is very hard to reason about a Ruby-based system for both users and e.g. computer optimizers. This indeed means a lot of tests need to be written.
It is also the reason that I really like Ruby for writing prototypes, or short command-line-interface tools. But for the same reason, I dislike working with long-running Ruby (on Rails or otherwise) applications: It takes a lot of discipline to keep your code clean in such an unrestricted programming context.
As for Java: Java definitely is somwhere lower on my poset of liked programming languages; mostly because Java’s approach to Growing a Language, and because of its enforced ‘static’ typing.
As one of my colleagues often says: Java is not so much a programming language, as it is a platform to build on top of, because it has such a large ecosystem.
Yes, new Java versions definitely make an attempt at making it a better language .
Let it be known that Elm is a fully pure language. (Especially since Elm 0.19, since it is now impossible to write your own Native modules)
I still see Elm as ‘Haskell-lite’, and that is meant in the most affectionate way. Because it did not have to care about being backwards-compatible with Haskell’s existing things, it was able to rename a lot of things, and make some different design decisions.
This point is wrong for two reasons:
- It has never been said that Elm will not ever support typeclasses. It is definitely true that they are not there right now, but depending on how Elm as a language develops, they might still be added. Evan Czaplicki is more concerned with keeping elm simple (frequently removing languages from the language, which is still possible in its current pre- 0.1 phase). It might very well turn out that typeclasses might greatly simplify some things. But there is definitely a lot of other stuff to implement first. (and building the compiler logic that allows typeclasses to exist, well, that definitely is hard ).
- Elixir has typeclasses. They are called Protocols.
With all that having said, though, currently, I am able to work professionally with Elixir (and Elm), whereas Haskell hasn’t found a place in my professional workspace yet.
But this is probably because we are mostly a web-development shop.
The reason then that we use Elixir, is because it is a very pragmatic language (as is Erlang; which, as Joe Armstrong has repeatedly said, only ended up as a functional language ‘by accident’ because it was the most practical solution for the problem that Ericsson had at the time).
In the end, all programming languages are a tool. Programming in Haskell (or in any language) is not the goal, but a means to build something truly marvellous.