Hacker News new | comments | show | ask | jobs | submit login

They're so different and yet so similar it's probably best taking them one topic at a time. I've written nontrival things in all of those except clojure, but I think I still have a pretty good idea what it's about from a bit of playing around, reading the notes of others, time spent in PLT Scheme etc. I hope this is helpful for some people looking to dive in--remember, all of this "IMO/IME".

Type Systems:

Haskell and Scala are closest in that they're strongly, statically typed with Hindley-Milner type inference. Erlang is dynamically typed as is clojure. This tends to break the advantage/disadvantage scheme along the same lines as Java/Ruby wrt typing: the former languages generally give you more speed and safety, and the latter languages can do cool tricks with deciding typing at runtime, which can aid in expressiveness.

Type System Complexity/Power:

Haskell has the most sophisticated type system--by far, I think it's fair to say. This adds power but at the cost of a steeper learning curve for people unused to thinking so deeply about the contracts/promises code and data structures are making. Scala's is also fairly involved since it's basically a subset of Haskell's (well, plus traits and inheritance blah)--but not quite as consistent or clear. Clojure and Erlang have somewhat simpler type system that may be more tractable out of the gate--Erlang in particular is pretty straightforward.

"functional-ness", how sharp a break from OOP/imperative is imposed:

Scala is by far the least dogmatic about doing thing in a "functional" as opposed to an OOP or imperative way. Scala's heavy emphasis on seamless interop with Java makes it a sort of up to the programmer to place their style on the continuum between something like Java and something like ML. Haskell, with its purity-by-default, is probably the most opinionated of the group. You will be forced to approach problems very differently. I'd say clojure follows just behind, also having a strong bias toward doing functional-only styles. Erlang is also pretty interested in doing things in a particular way that could be construed as functional (not rebinding names, focus on immutability, for example), but its "big idea" is more tailored around its flagship library and virtual machine.


Erlang will probably feel the most foreign to a 2011 programmer. Erlang started as a modified Prolog, and the syntactic legacy of Prolog is not widespread in common contemporary languages. Haskell's syntax is rather clean and restricted--though the fondness of veteran Haskellers for custom operators can sometimes be overwhelming for a new programmer. Scala, while a hybrid OOP language that shares many idioms with Java etc, has so heavily dipped into the operator well, even for standard language constructs--the syntax can be a bit crowded. Imagine Java with twice the non-alphanumeric density. Clojure is a LISP, so it's very clean and clear syntactically, provided you are a parenthesis master or make use of SLIME, etc.

Speed (average job, single core, not scalability etc etc):

Haskell, Scala, Clojure, Erlang--descending. Though on certain workloads these players will switch places, as a general rule of thumb that's my aggregated observed performance order in "real world-ish" code scenarios. Clojure is sorta interpolated based on experiences others have relayed to me and online. From a memory standpoint, Haskell is substantially stingier than the rest. This impacts startup times and the practicality of shell tools, etc.

Compilation/VM + Concurrency:

Haskell is alone in this group in that it compiles to native code--well, at least, the most commonly-used configuration does: the compiler ghc. GHC is a beauty of a project. Very good native code generation, aggressive improvement projects (new I/O manager, LLVM backend) incredibly advanced concurrency capabilities (sparks, first class green threads, seamless async IO). Erlang uses a VM, beam, which also uses green threads (which erlang calls processes) and transparent async I/O--but, erlang goes even further to provide syntactic support for super-efficient message-passing between these processes, and facilities to transparently pass those messages between machines in a cluster. Clojure and Scala both use the JVM. It is what it is--mature, fast, but primarily designed for Java and things a lot like Java. Cool features like tail call optimization and green threads are simply not possible b/c the JVM does not (currently) support them. (I know Scala has Actors, Akka, etc, but JVM-based actors are not in the same league as what Erlang and Haskell have.)

All of these languages have a REPL, thank god.

Breadth of Libraries:

The JVM languages here obviously have a HUGE wealth of libraries available. The only concession is calling them can be somewhat awkward and "downgrade" you out of the functional idioms you'd rather be using, depending on the library. Haskell's library situation is pretty good (Hackage has libraries for most everything you need and is growing quickly every day), and Erlang is probably bringing up the rear, here. OTP is excellent, and there are many libraries related to large networked systems etc, but not as much "general" coverage.

Key Strength:

If you want to write high-uptime distributed systems, Erlang/OTP is the bees' knees. Everything about the language and runtime is tailored to that environment--supervisor trees, mnesia, good scheduling of hundreds of thousands of lightweight processes.

Haskell is great at correctness. Like its non-lazy cousin ML, if you satisfy the type system, a shockingly high percentage of the time your program is just flat out correct the first time you run it. Haskell is also pretty darn good at single-machine multicore concurrency.

Clojure is a great "modern Lisp". Many of the concerns, fairly or unfairly, leveled at Lisps in the past (no libraries, stagnant platform, closed development, etc) are non issues. Building on the JVM enables a very small developer core to make progress on the language while leveraging a mature platform that evolves independently and at scale.

Scala is a "better Java". While iterop with large Java-oriented frameworks is possible in Clojure, it's pretty much trivial in Scala. You can dip a toe incrementally into the functional world while keeping retaining your existing projects, favorite libraries, 3rd party frameworks, etc--transitioning as fast and far as appropriate.


The good news is, all of these languages are pretty damn good, and all of them actually have healthy, thriving developer communities and razor-sharp maintainers.

If someone has never done functional programming before, I'd personally recommend trying something like clojure or Racket first to get the fundamentals down before digging into something truly mind-bending like Haskell. Scala is probably not a big enough leap to discipline yourself to grok functional just b/c it's so easy to relapse into non-functional patterns and Scala will happily comply.

Good overview; a couple nitpicks:

Technically Scala's inference is not Hindley-Milner since you still have to declare method argument types for JVM interop.

The speed difference between Scala and Clojure has less to do with the static/dynamic gap than the fact that Clojure relies upon reflection by default. However, once you identify your bottlenecks, adding type hints to remove reflection is pretty easy.

Good stuff.

A minor nitpick-nitpick: it's not so much that a JVM requirement prevents Scala from using H-M type inference, it's that H-M doesn't work very well with subtyping, which Scala supports for its own O-O reasons. This comment from Martin provides some context: http://www.codecommit.com/blog/scala/is-scala-not-functional...

Erlang is the shitzness! Not weird at all, very palatable, and thankfully, different enough to be a new experience.

> Haskell and Scala are closest in that they're strongly, statically typed with Hindley-Milner type inference > Haskell has the most sophisticated type system--by far, I think it's fair to say

HM is a type system first, and that means is a logic system. HM is a logic that is deliberately kept relatively weak in order to allow full inference. Scala is not HM and never has been - it's much, much more sophisticated and so Scala uses a form of type inference not based on HM. Haskell '98 is HM plus a tiny bit (and yes that tiny bit does mean that the occasional type annotation is required). GHC Haskell is HM plus a ton more and so there are a lot more GHC Haskell programs that need some type annotation. The people behind GHC Haskell have done a good job of carefully adding type system features that don't totally break the model, though, so you rarely have to annotate much unless you love playing in advanced type land.

I would hesitate before saying GHC Haskell is more sophisticated "by far". It's not at all obvious that that is so. I'd want a type theorist to spend some time with the two core logics to determine which one subsumes what parts of the other before making that call.

For correctness, both Haskell and Erlang have Quick Check, which is simply fantastic. Quick Check is somewhat easier to use with stateless code, which favours Haskell over Erlang.


For more correctness, scala has had interop hiccups



(and does Orig. Commenter have examples of clojure hiccups? Interop is very important to both (and F#). Maybe a more meaningful compare/contrast is haskell, scala and F# (which I can't do)




(his league table of "Most Powerful Type Systems": Agda, epigram, scala, haskell

On the contrary, QuickCheck is brilliant at testing stateful systems, as long as you can model the actions that occur and how they modify state (statem in eqc helps here).

Thanks. Please consider putting this comparison on a blog or similar, it deserves a wider audience.

Just adding to the list that Erlang has optional type specifications, used only for analysis. http://www.erlang.org/doc/reference_manual/typespec.html

Applications are open for YC Winter 2019

Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact