Haskell vs. Erlang, Reloaded

The goal of my project was to be able to thoroughly test a poker server using poker bots. Each poker bot was to to excercise different parts of the server by talking the poker protocol consisting of 150+ binary messages. The poker server itself is written in C++ and runs on Windows....

This app is all about binary IO, thousands of threads/processes and easy serialization. All I ever wanted to do was send packets back and forth, analyze them and have thousands of poker bots running on my machine doing same. Lofty but simple goal :-). Little did I know!

Erlang and Haskell compared... Want to know the conclusion?

I was able to finish the Erlang version 10 times faster and with 1/2 the code. Even if I cut the 10-11 weeks spent on the Haskell version in half to account for the learning curve, I would still come out way ahead with Erlang.

I am sure you'll find a lot to disagree with in this article...

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

Binary IO

Trying to do binary IO in Haskell with the standard functions is horribly painful. However, there are several libraries that seem to take much of the pain out of it listed at BinaryIo. This is surely something that will be fixed come the next revision of the Haskell Report. Obviously, everyone here should be aware that via bit syntax binary IO in Erlang is very nice. Simply using what was available from the get-go would likely have made a significant difference.

My 2 favorite quotes

Erlang is the specialist language narrowly focused on networked, highly-scalable concurrent applications.

Haskell is a specialist language for doing extremely complex things that few outside of the tight-knit Haskell PhD. community will understand or (heresy!) even care about.

Ignoring the heresy bit, I think he nailed a primary difference (in focus) between the two languages. That being said, I am not sure why he had chosen to go that far with Haskell when his application domain was so I/O intensive.

I've always considered Erlang and Haskell to be apples and oranges.

BTW, His conclusions on static typing, specifications and succinctness were quite confusing...

Haskell makes simple things difficult

Todd,

That being said, I am not sure why he had chosen to go that far with Haskell when his application domain was so I/O intensive.

I really wanted to learn Haskell at that time and the customer wanted an easy to use scripting language. Plus, they were concerned that their QA techs would have to learn the Erlang syntax. I got the syntax and the easy scripting language right but screwed up everything else :-).

It turned out in the end that the performance that I signed up to deliver was completely unrealistic which was no fault of Haskell. Still, re-doing the same app in Erlang was totally refreshing. Haskell makes simple things quite difficult sometimes.

BTW, His conclusions on static typing, specifications and succinctness were quite confusing...

What do you mean?

The Point of Haskell

Haskell's background, to me, seems much more theoretical and abstract than Erlang. I think that has a lot to do with its apparent approachability and ease of use, or perhaps lack thereof.

Even though Haskell, like so many other languages, is Turing complete, I think the users of the language never lose sight of its original purpose and direction. Haskell was written to be used in research, to be the "standard" for higher-order functional programming, and no matter what practical application you attempt with it, I think it will always strike people as dense and somewhat heady because that best describes to me its original intentions.

Whereas Erlang evolved with much more practical applications in mind. Fault-tolerance and network-friendliness make a lot of sense in a telecom environment, and that's probably where Erlang really shines. Again, you'll probably never get away from that aspect of Erlang, no matter what you write in it. But I can certainly see why Erlang is more approachable than Haskell.

From the Preface of the

From the Preface of the Haskell Report:

Goals
The committee's primary goal was to design a language that satisfied these constraints:

1. It should be suitable for teaching, research, and applications, including building large systems.
2. It should be completely described via the publication of a formal syntax and semantics.
3. It should be freely available. Anyone should be permitted to implement the language and distribute it to whomever they please.
4. It should be based on ideas that enjoy a wide consensus.
5. It should reduce unnecessary diversity in functional programming languages.

Except for (2), Haskell seems to have met all these goals. (4) is a very significant feature of the Haskell community which is luckily coherent enough for this not to stop new ideas. My point in mentioning it is (1); Haskell is definitely not intended to be a only a research language, but on the flip side it is intended to be for research as well. In practice, these turn out not to be very conflicting requirements as many of the theoretical ideas are imminently practical.

Confusing...

Joel,
I should have elaborated my point (upon re-reading my post, my comment is about as clear as mud)... So, I re-read your article and the confusion is probably mostly on my part.

One sentence that confused me was:

Haskell is supposed to be about declarative programming, I think. Haskell programs should look like specifications. Haskell is not supposed to be verbose and Succinctness is Power according to Paul Graham.

A declarative language strong on specification would naturally be verbose, yes? In your "pot" record, the Haskell code is explicitely specifying the types of the content. With Erlang, some of this has to be inferred: (is profit 64 bits or 32 bit)?

The records issue is a language issue just like static typing working against me with events. Part of the reason why the Erlang code is 1/2 the size of the Haskell code is that Erlang is dynamically typed. I just post an event of any type I want. I basically post tuples of various sizes but Haskell requires me to either use Dynamic or define the events in advance.

I can understand how "less code to write" is initially a big win, but was there any (later) point when writing the Erlang code where you weren't quite sure of your types were that you manipulating? (This is not to invoke the boring old dynamic vs static argument, but you either pay up front or potentially later. It took me much longer to code a network server in Concurrent ML than Perl, Tcl and Lisp, but I was amazed at how robust the code was once I got the sucker to compile and run. Now, one could argue that Perl/Tcl/Lisp network code is easier to debug and this may be true. But, I like to think that the verbose "specification" in ML helped me in the long run).

I understand that Haskell made some things harder (I evaluated using Haskell for some of my network servers and found that it would be way too much work!), but isn't that the nature of the beast (precise specification)?

Overall, I really like your article. I am a big fan of Erlang and a distant admirer Haskell. I don't work with either (doing some OCaml right now). I work in the trenches and its good to see FP being used by others in the trenches too!

As Joel, I and a number of

As Joel, I and a number of others discussed on #haskell, part of the problem is that haskell's type system is poor at typing the kinds of things he was doing. For example, given n message types and m entities accepting o combinations of message types, there is no good solution for typing this accurately while retaining pattern-matching even with GHC's extensions - it can be done, but at the cost of much unboxing and reboxing and a type syntax that requires an indirection (or a good coding convention) to figure out which message types're actually accepted.

I'd like to hear more about

I'd like to hear more about this - I don't really understand the issue with static typing, particularly since Haskell infers types. What was wrong with the general "data Event = ..." approach, with one constructor for each event type sent? It seems like an Erlang tagged tuple directly corresponds to a Haskell algebraic type (the tag atom is the constructor, and then the other elements are the arguments). Does Joel go into this any more in-depth anywhere?

What's wrong is that it

What's wrong is that it gains you little more than dynamic typing does. You know you've got an event, but you've no clue if it's one of the ones you handle.

Session types

I wonder if playing with session types might have been interesting: a google search. In this case, it may not have been appropriate at this point because I'm not sure how mature any approaches are, but it might be interesting to see how it might work out.

Declaratively succint

I can understand how "less code to write" is initially a big win, but was there any (later) point when writing the Erlang code where you weren't quite sure of your types were that you manipulating?

Not at all. I never had a problem with this. In fact, I never card about this when coding in Erlang.

I second your point about the robustness of the resulting Haskell code. I said in some other post that if it builds then it's probably right. My pet peeve, though, is that Haskell seduces you into coding for clarity and beauty. Haskell seduces you into coding the natural way. The natural way is concise since you can let type inference do the work, use lists, etc.

Then reality kicks in and you realize that the code is not running fast enough or consumes gobs of memory or spends 70% of its time collecting garbage. That's when you start putting strictness annotations all over the place unrolling your lets into cases and so on. It gets ugly.

Now, there are masters out there like Oleg Kiselev (Zipper OS) or Simon Marlow who probably don't have any trouble at all writing code that's both concise, beautiful and fast. I'm not one of them, not with Haskell. I find that doing things the natural way in Erlang hides no surprises and runs reasonably fast. Same thing about Scheme as I just set out to prove.

When reality kicks/knocks

I'd love to hear folk's real-world experience with getting a beautiful Haskell system to run fast. What are the right tricks to employ to end up with something still beautiful vs. the 'hacks ' that make it filthy (strictness annotations)?

And, are there other languages address this kind of thing? Are there other beautiful languages that can be fast? (Clean? Oz? *ML?)

OCaml

It is my impression that OCaml can be quite fast, but I can't speak from personal experience.

Erlang-style Distributed Haskell

'kay, a shameless plug, but maybe you'd have preferred having the best of both worlds: DHS

Volker

DHS dead?

That link is dead. Googling for "distributed haskell" finds some pages (Glasgow) that also make it sound like nothing around that is being kept alive / up to date. Anybody know if it is solely an ex-parrot (a la goffin)? Or is it all solved and finished and great and that is why the pages are all from 2003?