Update Simon Marlow mentioned this article in a Google+ post, there was a lively reddit discussion about this article and this article was published on i-programmer.info. Also, one of the people working on rust shares my sentiment on Kotlin.
Apart from coding in Java for university projects and briefly checking out the one or other language I’ve been toying around with Haskell since round about one year. Some of the impressions I had on Haskell can be found in my previous post Experience Report: A Mastermind Simulation in Lua, Java & Haskell. I’ve had a lot of praise in respect to some of Haskell’s virtues but at the same time I acknowledged its warts. The more I programmed and thought about the things I want to create the more precise my ideas of what I want from a language became and the bigger the warts seemed to me.
One of the negative things I pointed out previously was the dreadful record syntax. Accessing (nested) fields is a nightmare and updating them even more. Not even lenses come close to the syntactic ease of field manipulation of conventional programming languages in my opinion. What’s even more tragic: everybody in the Haskell community is aware of this but despite endless discussions for years a solution is not to be seen on the horizon yet.
Lazy evaluation by default has bitten my arse in every single project thus far - and there weren’t all that much projects nor were they particularly complicated. Sometimes I searched eight hours for the source of a space leak. Other times I had to really dig around the internet to learn how to force a computation so that I could measure the time of said computation. Each time I increased my “defeating laziness skills” but for the next “lazy” challenge my skills weren’t good enough and I had to play the role of a detective again which in fact wasn’t as fun as it sounds whatsoever. And even though I acknowledge that there are beautiful things / theoretical advantages that are allowed by laziness, personally, I find it to cost more than it helps and I find it telling that even the brightest Haskell hackers have to tackle so much with space leaks. One of Haskell’s creators has actually released slides with the title The next Haskell will be strict. At any rate, I’m not the only person that dislikes laziness by default and there is a lot more that could be said about this topic.
I don’t have the source for the following paraphrased quote but I’ve read it a couple of times in the Haskell mailing list:
Haskell makes some easy things harder and some difficult things easier
In some scenarios I wished for the good old for loop instead of e.g. a fold which is arguably as “low-level” as a for loop or some not-so-clear combination of some local or inline function definitions and compositions of several combinators. Now I know you can mimic for and while loops in Haskell but then you are in monadic land and anyhow - they are syntactically and semantically not quite the same. What this point actually boils down to is that I want a language where immutability as well as mutability are treated like first class citizens. Let’s be honest: if you want to do some things that are easy in conventional, imperative or “unpure” languages prepare for a syntactic and semantic overhead of concepts in Haskell. It’s not that it’s too hard to understand how to do it in Haskell but rather too cumbersome in my opinion. Here’s a sentiment that I stumbled upon on one of the soon-to-be-presented languages (Disciple) and that I share fully:
We still think the Monad type class is useful, and we support it as well. However, when writing Haskell programs we’ve found that most uses of Monad are for IO, and to manage the internal state of the program. In Disciple, we use effects for state-based functions because it’s more convenient, and reserve Monad for things that definitely want a non-standard notion of sequence, like parser combinators. Source.
There are other problems that could be talked about like
- the overwhelmingness and confusion of new concepts: conduits, iterators, enumerators etc.
- package dependency hell (which I experienced fully)
- standard libraries could be more consistent (e.g. only map, not fmap…)
However, I think I made some of my dissatisfaction with Haskell clear and in light of that I hope it is easier to understand how I judge the later presented languages.
A New Hope
Due to the problems I observed about Haskell I finally understood the appeal of languages like OCaml, Scala or F#. Broadly speaking, they are similar to Haskell in that they emphasize functional paradigms but they are strict, have first class support for mutability (apart from immutability) and imperative programming paradigms. Of course each of them comes with its own baggage of new concepts and ecosystem-wise there are quite a lot of differences. So far I’ve tried to play a bit with Scala again, but.. I can’t really point my finger on it but I can’t like the language. It’s probably because Scala seems to be even quite a bit more complex than Haskell and there seem to be so many ways to achieve a task. F# seems to be too .NET centric and OCaml seems to have its share of own problems but that doesn’t mean that I don’t want to check it out someday.
I think now is a good time to explain that I had a small to medium project in mind that I wanted to implement as elegantly as possible. In order to compare the different languages I tried to implement it in some of them. I first started with Haskell. Algebraic datatypes, immutability, the help of the type system, automatically derived type classes, type classes in general and some syntactic virtues of Haskell made the beginning of the project really pleasent. However, when it came to state shuffling monadic code paired with the unforgiving record (updating) syntax I soon felt that the elegance was gone. Next in row was Java. Ironically, using the “unsexy” Java language I almost fully implemented my project which hasn’t been the case for the Haskell version although I really felt that missing support for algebraic datatypes, immutability, null pointer safety, first class functions & closures, conciseness and the interlocking of namespaces, classes & files made a lot of things unnecessarily cumbersome and unclear. For instance, I had to manually provide copy, equals & hash methods, I had to make classes like Pair due to a lack of tuples, had to create classes where a simple closure would have sufficed etc. Still, the uncumbersome way of writing down control flow, post-fix accessing of methods/fields on a class, great IDE support and first class field manipulation support made up for the weaknesses, that is, just so much that I still had motivation to almost finish the project. I also tried to implement it with Scala but I ran into some problems that I could not solve elegantly so I didn’t get very far.
Well, it turns out there are some interesting new alternative programming languages that make some of the wishes true or at least are able to improve some of the aforementioned pain points. Let me just list them and afterwards talk a bit about each:
Idris & Disciple are quite close to Haskell. I don’t actually know much about each of them apart from them being syntactically similar to Haskell but strict-by-default. I only read about their features on their respective homepages.
Idris is a general purpose pure functional programming language with dependent types. Dependent types allow types to be predicated on values, meaning that some aspects of a program’s behaviour can be specified precisely in the type. It is compiled, with eager evaluation. Its features are influenced by Haskell and ML.
I never used a dependantly typed language but what I understood broadly is that you can encode more propertiers about your program statically in the type system. The advantages notwithstanding, this seems to lead to farreaching implications regarding the practical usefulness of a language with respect to general purpose programming. Idris wants to find the sweet spot between practicality & power of the type system. In any case, I mostly listed the language here as a reminder for me to check it out sometime.
Disciple is a dialect of Haskell that uses strict evaluation as the default and supports destructive update of arbitrary data structures. Disciple includes region, effect and closure typing, and this extra information provides a handle on the operational behaviour of code that isn’t available in other languages. Programs can be written in either a pure/functional or effectful/imperative style, and one of our goals is to provide both styles coherently in the same language.
Disciple is not a dependantly typed programming language. It is very similar to Haskell in that it even claims on its homepage that a lot of already written Haskell programs would be valid Disciple programs albeit with some minor modifications here and there. The characterising feature of this language seems to be an effect system. Without understanding what an effect system is or how this or that is implemented one can still take a look at the language overview on Disciple’s homepage and learn that seemingly the main complaints of mine regarding Haskell above are solved in Disciple. I.e. strict evaluation by default with optional lazy evaluation, first class mutability support, first class field manipulation support. Disciple seems to be great at first glance.
Let’s be honest for a moment though: There is no corporation behind Disciple (nor Idris). This isn’t a problem per se but it means that building an ecosystem around a language (including a user base) will take much longer than the other way round. It eventually worked out for languages like Python or Ruby but it took them quite a while. So, I’m remaining unhappily skeptical if at all or when there will be a big enough ecosystem so that one can use the language for all the tasks which can be accomplished with a mainstream language like Java today. With that in mind let’s take a look at the following corporate backed programming languages:
Rust is a safe, concurrent, practical, curly-brace, block-structured expression language. It supports a mixture of imperative procedural, concurrent actor, object-oriented and pure functional styles. Rust also supports generic programming and metaprogramming, in both static and dynamic styles.
Rust is backed by Mozilla. In some of my previous posts I already mentioned my exciteness about Rust. Not long ago Rust v0.1 has been released so I had the chance to play a bit with it. I actually implemented quite some bit of the aforementioned project and it was fun. On first glance, Rust felt like the perfect blend of imperative languages like C and functional like Haskell or OCaml while still feeling really lightweight. I especially like the structural records, interfaces (like type classes in Haskell) and the separation of namespaces without overloading the .-operator. Rust also has some more or less unique features regarding the control of memory layout but I did not care much about them although I acknowledge them to be an advantage.
Let’s turn our attention now to three languages that are closer to Java: Dart, Ceylon & Kotlin.
Dart is a new class-based programming language for creating structured web applications. Developed with the goals of simplicity, efficiency, and scalability, the Dart language combines powerful new language features with familiar language constructs into a clear, readable syntax.
Ceylon is a general-purpose, imperative, statically-typed, block-structured, object-oriented, higher-order language featuring a syntax similar to Java and C#, and a type system based on the notion of principal types.
Ceylon is backed by RedHat. To be frank, I’m just listing Ceylon here for completeness sake and because it is another example of the aforementioned stream of new programming languages. Goalwise it is quite similar to Kotlin although broadly there is more focus on immutability. On the whole I find Kotlin better designed and personally attribute Kotlin more potential although that does not mean that I find Ceylon bad. Not at all, it’s great but the competition is great as well. Maybe I should use the opportunity and list some other (JVM) languages which have similar goals to Ceylon & Kotlin but have for one reason or another not managed to become a significant mainstream Java alternative yet although there are niches where some of these languages excel: HaXe, XTend, Gosu, Fantom, Groovy, Scala, Clojure, …
Kotlin seems to generally make most of my wishes true - of course not without some trade-offs. I also tried to implement the aforementioned project in Kotlin and although I didn’t come very far simply because most of the features that would have been great for that project weren’t implemented yet I already could see that the eventual program would be in the same elegance ballpark as Rust. Concerning the unimplemented features: indeed, there is a lot of stuff not yet implemented in Kotlin but the suggestions and ideas are there and they seem very promising. Altogether, I find that Kotlin finds the sweet spot just between being innovative / brave enough to introduce new syntax & semantics to encode common programming patterns and being familiar enough to get going with it quickly. I especially like the “zero-overhead null safety” in Kotlin which provides null safety without needing to use an option type. There are a lot more interesting things to find out about Kotlin (e.g. builder syntax) so take a look at their website if you’re interested.
Conclusion & Comments
Again, all these listed languages are not in a production ready state yet but I’m going to check back in approximately six months and I hope that by then most of the rough corners will have been polished. I think it obviously came across that I like Kotlin & Rust best and so naturally I hope that these will be extra polished by then. Maybe you wonder why I didn’t mention any current or upcoming dynamic programming language? Personally, I came to the conclusion that a type system helps tremendously when creating, modifying & maintaining programs. Anyway, I didn’t think I’d write that much but alas, I did. I hope it was a bit informative at least. This post is personal opinion so please don’t take things I’ve written here as absolute.