It's a pity I don't use Haskell at work and yet learning Haskell was the single most bang for buck exercise I have ever done. "Parallel and Concurrent Programming in Haskell" is the best resource I have ever read on parallel and concurrent programming concepts.
Every programmer should learn this language even if they never plan/get to use it.
I learned Haskell and used it (and heavily functional Scala) professionally for about 6 years.
I feel learning Haskell was worthless. It did not teach me anything about decomposing problems into function units, compositional sequences, pure functions / immutable data structures, lazy evaluation or asynchronous programming that I have found useful in modeling solutions to software problems.
Guarantees from the compiler and use of the type system itself for type designs like phantom types and type class patterns did not help reduce behavioral bugs and the slower development cycle of waiting for compilation (instead of just using occasional unit test runs to check simple safety conditions in lighter weight dynamically typed languages) was a very significant cost.
It was no easier, and often much harder, to enable extensibility into systems designed in Haskell because the constraints of the extensible needs of the system were only ever learned from changing business circumstances after the fact and could not be reflected in anticipated up-front design, which is perhaps the number one need of business software yet is an area where functional languages are uniquely poorly suited even with veteran, highly experienced engineers on the team like we had.
In the end, following simple patterns of composition and function purity while avoiding cookie-cutter design patterns and avoiding use of object / class constructs is all you need. This is easy to achieve in many imperative languages while leaving much greater flexibility to choose mutation, treat safety as a resource to be traded off instead of enforced in all cases, etc.
“Module oriented programming” in Python or C, letting lightweight unit tests replace heavy reliance on type system patterns, just using simple structs / namedtuples / dataclasses that posses zero function logic, and creating imperative APIs around functional-inspired core implementations is strictly better.
Really, strong adherence to statically typed functional languages with lazy evaluation is a religious trap. Learning that it’s not all it’s cracked up to be early in your career is critical for success.
Type system designs in statically typed functional languages are intended to represent increasingly abstract correctness constraints by transforming the business logic of the problem at hand into rules subject to the validation of the compiler.
This is quite different than object oriented or even compiled imperative languages where type system constructs contain data and function units that represent internalized constraints.
In functional programming you set up types and their constraints such that the business logic you require is a derivable consequence of the system and any other outcome is as close to provably impossible as can be. The less of this you choose to enforce, the more “imperativy” or “objecty” your use of that functional language is.
In imperative or OO languages you choose to make atomic units that have internal structure, but whose behavior across the interaction of the units is not itself a derivable consequence of any more abstract set of rules. Not even interfaces or templates represent this - they govern how components can communicate but do not limit what components can do.
You use the program’s structures to carry out the required business logic, but you cannot truly prove you haven’t allowed for some different logic or edge case instead.
In this sense, safety is a resource rather than a requirement, and if you learn that a previously unsafe operation is now safe, because the real world circumstances changed around you, you are free to have your program units do the previously-unsafe -now-safe thing without needing to remove expensive abstractions that had been set up to cause the previous notion of safety to be a consequence of your program.
Sounds like you would be better suited using some form of dependently typed system as far as type systems go.
If I understand you correctly now, a functional language with no real type system (and thus no such compiler enforced constraints) like Elixir would be imperative in your book and therefore more suitable to facilitate constantly changing business requirements.
Thanks for taking the time to detail your thoughts for me!
Because these language wars are mostly about people defending their group identity that they've invested a lot of effort into forming. If you want to have focused, fact-based discussions about language tradeoffs, it's more likely to be successful if you stick to small groups of people who are mindful of their egos.
This is a fact-based discussion of language trade-offs. The strength of a statically typed functional language, that you are forced to specify correctness of the program at increasingly abstract levels of the type system, is also its downfall because this is inherently inflexible if the business use case of that software suddenly demands incrementally mutating some part or introducing subtle violations of the constraints the system was designed to enforce. The entire goal of encoding correctness and rendering it difficult or impossible to violate a given notion of correctness is intrinsically at odds with the business purpose of software. This is fundamental. It is a real, physical problem that makes strict functional languages significantly worse suited to business settings than imperative or even object-oriented paradigms, and is supported by language popularity, adoption and success rates.
The fact that classes of bugs prevented by compilers are not very important and are just as adequately caught with lightweight unit tests is more empirical but critical nonetheless.
These are real problems with the arguments in favor of strict functional programming. But instead of engaging with them, they are disingenuously called “language wars” as if they are less legitimate just by using this phrase to describe them.
> This is a fact-based discussion of language trade-offs.
I'm sorry, but it's an opinion-based discussion. No hard facts were presented here. While I see the reasoning behind such opinion, it directly contradicts opinion of pure FP proponents and somewhat contradicts my own experience.
> the business use case of that software suddenly demands incrementally mutating some part or introducing subtle violations of the constraints the system was designed to enforce
Sure, sometimes it's very convenient to add quick hacks, but there is definitely a tradeoff from maintainability perspective. I wouldn't like to work on a codebase full of mutable state and constraints broken in unprincipled ways.
On the other hand, I never used Haskell and religiously functional Scala, so maybe I underestimate the scale of the problem. For me Scala with immutable collections, ADT, lenses, IO, typealiases and typeclasses as extension mechanism served extremely well. Could you please provide few concrete examples of type system abuse?
The GP meant that the comment was downvoted because of "language wars" downvoters, not that it was language-wary itself. It did take me two readings to realize that, because I was going in with my own bias.
This is my same experience except with Clojure. It’s pretty much equivalent to well written JavaScript. Actually no, the latter is usually cleaner and clearer.
That's interesting. I reached a similar conclusion, but I entirely credit learning Haskell for getting there.
I thought I had some idea about controlling state and writing composable systems before, but I see now how rudimentary and flawed it was. Maybe you just got it quicker.
Jobs that exclusively or almost exclusively used Haskell & Scala: (1) backend engineering for a large bank (you can likely guess), (2) machine learning at a large education technology company, (3) machine learning at an ad tech company.
My favorite two languages are Python and C. There are certain things I like about Rust, Go and Julia.
I dislike Java and C++ (just personal preference).
I dislike Scala a ton. Really disergonomic and overly complicated.
I like Haskell a lot. Very clean, fun to write, it just doesn’t confer any serious advantages over other language choices and does suffer from difficult-to-extend designs in business use cases, but probably very good for academic work, pure software or research projects. It’s very nice language, I just wish it wasn’t hyped as a transformational way of thinking or as a panacea for all the normal annoying problems with business software.
If I am not mistaken, Dijkstra advocated for Haskell to be used as a language of instruction at universities. There is an essay somewhere online about it. What I could find is this: [1].
Edit: Indeed, the PDF that is linked to in [1] looks like a scan of the original. [2]
> Colleagues from outside the state (still!) often wonder how I can survive in a place like Austin, Texas, automatically assuming that Texas’s solid conservatism guarantees equally solid mediocrity. My usual answer is something like “Don’t worry. The CS Department is quite an enlightened place...
Very Dijkstra to start by insulting the intended audience. Wonder if it had the intended effect?
Note that Dijkstra recommendes Haskell over Java because Haskell is arcane and mathematical, which is good for learning computer science, not because it's good for writing software.
Your paraphrasing of "novel" as "arcane" is in bad faith. It is also self evident that djikstra believes that learning computer science is good for writing software.
I agree with this, learning Haskell was transformative for me. It changed the way I approached problems in every other language. I treat learning it as one of the 2 or 3 things that has most influenced my development skills.
But the truth is that I still don't feel comfortable writing anything more than toy apps in Haskell. I'm not really sure how to unit test things when I'm passing complex monad transformers around. I feel like I'm just guessing at best practices for arranging, say, a website of moderate complexity.
There's tons of resources for learning the language itself, but very few on actually applying it successfully to anything other than a single purpose command line app.
From my experience the key to becoming comfortable writing "real" apps in Haskell, or any language for that matter, is to just do it and keep at it. The easiest path, and the one I chose, is to do it full time as your day job. When you write Haskell all day every day you'll get comfortable with it surprisingly quickly. You also realise that it's not a panacea, it's just a language but a damn fine one at that. You can (and should!) write super simple, unceremonial Haskell the vast majority of the time if you are writing "real" apps as opposed to pushing theoretical boundaries.
Re code architecture and arranging code - It's not that different from what you'd do in any other language and it's more of a domain question (a webapp?, a game?) then a language one. If and when you change your mind though to move things around/re-architecture your code that is when Haskell really shines. Compiler assisted refactoring is one of the superpowers. It's like having google maps in a foreign city, it guides you to whatever your destination is.
I'm probably a lot lower than you but I feel I hit a similar wall. I naturally end up writing a function soup which feels super dirty.. as much as I dislike the term (uml-overload) there's a need for architecture that I don't know how to address in FP.
A few years ago I accidentally deleted my entire Haskell assignment about 1 day before it was due.
Within a few hours I rebuilt it from scratch.
That's when I realized the power of functional programming. There's no way I have been able to recover that quickly if I had used an imperative programming language.
Since that experience I've tried to follow a functional programming style whenever I can. I've realized that every variable is another state which adds to the complexity of the code.
But it was a programming assignment. Building a thing fast is, outside of very few situations, not a thing to optimize for. Building something to be maintained and evolved over time by non rockstars is far far more important, IMO. Perl is great for ripping through some script quickly but it has problems for long term maintenance.
Though the interesting point here was that it made it easy to write the program fast the second time. While it should be very rare you need to write exactly the same program a second time, and can be a useful thing (albeit a hard thing) to optimize a language for if in writing a program a first time in that language you wind up with such a clarity on the problems you encountered that "solving" them again a second time feels trivial.
I like this reference style of documentation, it tries to give a full overview of everything there is. Which is usually what I'm looking for. The sections are also concise.
If you want to learn a functional language absolutely do not learn Haskell.
If you're interested in static functional programming, learn Elm and then F#/OCaml.
If you're interested in dynamically typed then learn Clojure or possibly Racket.
That's it. Spending time learning Haskell as your first (or even second) fp language is a terrible idea and has done more to slow FP adoption than anything.
You can learn Elm literally in a weekend it's so small and well organized (go to their site and follow the tutorial). The concepts you pick up will double the speed you learn any other static FP.
F# on .net core is cross platform and can run via a vs code plugin. Plus you have a batteries included full class library of practical needs via dot net.
Clojure is very productive, great concepts as well. A bit worse tooling / debuggingqgen you have to drop down to deal with java, but also had batteries included java libs.
Racket is probably the most isolated of the above. A scheme dialect and a bit closer to historical scheme and Lisp.
Don't learn Haskell it's a horrible use of time as a first language. You get 80% of the value in the above languages in somewhere between 1/10 to 1/4th the effort (depending on which lang you choose), and have learned a more practical language, and will actually learn that lang plus Haskell faster than if you had started in Haskell.
I’ve tried learning Haskell once or twice and it didn’t really click. However, I’m know knee deep in learning Lean, which is a dependently typed functional language that is also a theorem prover and it’s been a much easier ride. It’s not any less complex than Haskell but learning FP by essentially doing mathematics has made it a lot easier for some reason.
Agreed. Haskell is masters class in functional programming that is generally more interested in pushing theory forward (not necessarily a bad thing). If you are a .NET developer, F# is a wonderful language (based on OCaml) that allows you to ease in to FP since it supports functional, oop, and procedural programming. Plus, you have the whole .NET ecosystem as well. You can become very productive, very quickly.
Haskell is very much the deep end of learning functional programming - it's at an extreme end of the spectrum in purity and type system.
The language I recommend as a first step to most people is Elixir (or Erlang). It's fairly pure, data is immutable, relies on recursion at the lowest level etc. Good code in Elixir is structure in a similar to good code in a lot of other functional languages (and so teaches good habits), but being dynamically typed is avoids that immediate pain of learning about how to keep the compiler happy.
As a counterpoint, I picked up (some) haskell pretty quickly.
I read a lot of code and didn’t get bogged down reading explanations of what a monad is. I also avoided trying to understand too well what the evaluation semantics of the language were.
When I read code, I focused on looking at the type signatures and understanding what they meant, then I would stare at the code and try to work out in my head why those definitions would get those types.
I did this at a time when there were lots of “I wrote a program in Haskell. Let me explain how it works” blog posts on this site.
I defined some data structures and solved a bunch of project euler problems as practice
My experience is that people get scarred of so much new terms which get introduced in Haskell and that could feel overwhelming.
When beginning with Haskell, I would advice to just write code and try to intuitively understand bits, but not get down into unwrapping things or theory much. Stay high level and figure out how things interact as you would do in black box model. Don't open the box, but poke it and see what result you will get (in other words; just write code and do trial and error).
When you get comfortable with black-box learning then open the box and look for the details.
I see a lot of people below saying to chose a functional language other than Haskell. As someone who's currently learning Haskell, I'm finding it _incredibly_ interesting...but I've heard that many of the free online resources don't lay the necessary foundation for learning the more advanced concepts.
I'm 6 chapters into Haskell Programming from First Principles [0], and I highly recommend it.
Haskell is fun, but bear in mind that it's followes a purely functional programming paradigm, meaning you either do things the Haskell or you don't do them :). But it's a fun language to learn, and there are lots of free quality ressources on the internet
What do you mean by "either do things the Haskell (way) or you don't do them"? Isn't that the same with every language? I still don't understand why people put all sorts of unfair labels on Haskell. By labelling Haskell with "fun", we almost make it sound like it can't be used in production.
What I think he meant is more like "either do things the functional Haskell way or you don't do them", as in, there is no non-functional fallback or alternative in the language, which some functional languages have.
There's escape hatches here and there. If you need imperative code, use IO or ST. If you need to debug output something, Debug.Trace.
If you need to temporarily bypass purity there's unsafePerformIO and friends (or they can occasionally be used permanently if you do a lot of analysis I believe).
It's a pretty practical language compared to its reputation.
Somebody said it, but it's glib, and not particularly true. I believe it was a comment on do notation, the bind operation, and programmable semicolons. The customizability is definitely cool, but nobody could possibly argue that imperative programming in Haskell is ergonomic.
Sure, IO and ST are escape hatches, but they're not escape hatches that beginners struggling with the concept of a higher order function are going to be able to use.
I imagined it was more in the sense of the 'Have fun implementing Quicksort!' variety, where you're fighting the language constructs, basically, to do what you want.
Haskell has a huge catalog of libraries you can reach for, so you don’t have to reinvent the wheel. That said, the Haskell way can seem very different than what you see in most languages. As an example, IO—or anything causing a side effect—has to happen in the IO Monad. There are good reasons for this, but it takes some getting used to.
But let's say I want to print something in a deeply nested function which isn't of the return-IO-Monad-type. Now I have to convert the entire call-chain of functions into Monad style. Is there some tool to do this automatically?
For debug there is Debug.Trace which is about perfect.
For non-debug, you may be overestimating how useful it is to output from pure functions in production.
In general when you are in a situation where you need to change the Monad you're under, there's techniques and libraries for that (one way you define it once, give it a name, etc.).
The biggest pain tends to be going from pure to monadic in the first place, which last I used Haskell there wasn't much for but to just do it. There may be more tooling now, haven't seen.
Because some non-technical manager came to you and said, "Can't you just print out the value here? And can I get it in the next 15 minutes?" and it was pretty clear that "No" was not an acceptable answer.
Different things are hard or easy in Haskell compared to other languages. Mergesort would be the first one I'd reach for in Haskell if I'm implementing from scratch.
Quicksort isn't really hard, you just do it in IO and it looks about like what you'd see in any imperative language. (or you can use ST, but then you have to know about ST).
Performance. Internal representation.
You are not actually modifying things in place. Those are the equivalent of linked lists not arrays. That quicksort is not quick at all because the append operation in Haskell is linear. You can't use those lists to implement quicksort.
Haskell is lazy. a ++ b === (head a) : ((tail a) ++ b) (plus the base case)
It is only linear when you force it to evaluate, which ideally happens only once (amortized) in your program when the result is consumed. As long as you are careful, you don't get the bad quadratic performance that you'd get from something like eagerly appending a character to a string in a loop in Java. There are either gotchas, though, including relying on the compiler to do things in constant space that naively look linear, and also knowing when the compiler won't help you and you have to structure your computation manually.
“Modifying things in place” is, from the logical standpoint, an unnatural and even dangerous thing. A person that is not familiar with (imperative) programming often has a hard time understanding variables and assignments (similar to some programmers not understanding pointers). Mathematics doesn’t have assignments, either. That should tell you something...
It mostly tells me that mathematics does not care about runtime or space performance. Which is fine, because mathematics is about solving abstract problems, not getting a Turing machine to solve concrete problems.
Won't this have to iterate through linked lists and create multiple new heap allocations on each recursive step? Doesn't that imply n log n heap allocations just to sort a list?
Yes, but due to lazy-evaluation that won't happen until you do something with the sorted list. Your performance may just blow up in some seemingly unrelated place.
My biggest problem with Haskell is how GC and lazy-evaluation makes it very difficult to reason about what the hardware is actually doing at a given point. I know there ways to inspect and control it, but I've found myself preferring languages that have simpler mental models.
Obviously picking first element as pivot makes for a very poor Quicksort. Though I presume a better approach, say median of first, mid and last elements, would add but an extra line?
The reason the first element is used as the pivot is that these are linked lists, not arrays, so it would take linear time to access the middle and end. This isn't a real quicksort.
But you do have to invent something or you wouldn't be writing a program in the first place.
As soon as you need to control the order of execution, memory usage, IO, or any combination of those, you are fighting the language and into territory of things you can technically do and out of the realm of things the language makes easy.
I wish I could give you a thumbs up for speaking plain truth here. Anyway programmers will find out for themselves how best to satisfy their customer need.
> it's followes a purely functional programming paradigm
I'd say that it's one take on that paradigm, the Way of Monads is not the only way to do purely functional, Clean uses uniqueness typing instead. Though it otherwise has rather similar properties to Haskell.
I'd say start with some eager functional language such as OCaml without the "object" part. Haskell code is hard to debug cuz you can't rely on good old print.
Monad itself is never a silver bullet. Type systems and Composability are the true power of FP IMO.
Debugging declarative code is always hard, whatever the language. Haskell does allow you to use trace expressions which will output values to standard out, see Debug.Trace.
This is no reason to avoid arguably the most popular (and state-of-the-art) function language and implementation out there.
It took me way too long to come across Trace, if it was more widely known I don't think we'd see so many "impossible to debug" related issues. It's great.
I think we can all agree that debugging languages with lazy evaluation is specially hard. To effectively use Debug.Trace you actually need to add strictness and that can be fundamentally incompatible if you do intend to use the full power of lazy evaluation.
Debug.Trace won't solve the space leaks lurking around every corner of your program, and won't shave the hundreds of megabytes to gigabytes of RAM the compiler needs for compiling a non trivial program with dependencies.
I like to think that ML languages, in general, are good to start to learn FP with.
SML by Dan Grossman is a great resource because it explains the concepts very well. Anything that you learn after will be easier, does not matter if you go for Haskell or F# later.
Picking the point where I gave up, giving me a list of 7 vscode plugins, without any guidance as to which are more mature, doesnt feel like a thing anyone would wish to know before they start.
No one said it's what they would wish to know before they start.
Take it from someone who spent 10 years on and off learning Haskell:
It's a guide to navigating the boobytrapped minefield of the Haskell ecosystem, from the difficulties of installing it for the first time, to the confusing type theory.
Those aren't 7 plugins to choose from. Those are 7 plugins you should use all 7 of because there is no unified coherent Haskell IDE or plugin, but there are a dozen other broken ones that look good in the catalog.
Much the same with libraries. You need a guide to tell you which ones are usable and which are abandoned experiments, since both live side by side in the main repos.
It is under active development and is the closest you can get to a "unified coherent Haskell plugin" - it comes with all the features you would get from installing the 7 plugins individually.
I have to agree. A more opinionated guide would be more useful than one full of so many long lists of alternatives; a learner is in no position to make those choices.
Is there maybe an article somebody can actually stand a chance of reading end to end? I think GP is right, there’s not a chance somebody who’s actually new to Haskell would find this useful as a true “things I wish I knew” article. It’s more of a complete reference to the entire language.
To me at least, “things I wish I knew” implies it’s some sort of companion to a real language guide/tutorial/book, not a replacement for one.
I'm sure you are right. That is just picking at one thing I will admit, but I do use vscode, so when I saw a section about how to best use haskell in vscode i was interested. I'm not interested enough, or as a beginner qualified, to evaluate 7 options.
The IDE problem is still a major pain in Haskell. However, for now there are just I think 2 main options to choose from, and it's fairly straightforward. Still, not everything works as expected yet.
We've come a really long way in just the past 3 years with IDE stuff. I suggest you give it another try if you're still interested in diving into Haskell.
What's the point of using 7 VSCode plugins for a language you don't even know? Just pick up any terminal, any text editor, and the freshest stable copy of GHC.
The VS Code plugin listed (haskell-ide-engine, https://github.com/haskell/haskell-ide-engine - to be more precise, it's a language server supporting many editors) is probably the one which sees the most development at the moment and comes with many of the other plugins listed or equivalent functionality (syntax highlighting, tab completion, linting via hlint, code formatting, refactoring) included. It certainly is the tool I would recommend to VS Code users.
This should be the #1 recommendation to anyone looking to get started IMO, it cleanly sets everything up for you and extracts it into an isolated development container.
I find myself coming back to this for various stuff all the time. It's a great reference that touches basically everything important for writing real apps with Haskell.
If you're thinking of learning Haskell you should consider the software written in haskell as an indicator of, well, something.
List all the programs useful for something other than programming a computer written in haskell:
Xmonad window manager
Git annexe
Pandoc
What else, let's get the full list. Exclude anything that we can't directly see or use.
I say, yes! Learn Haskell! Just don't expect to write any useful programs because they're pretty rare for people to have written when they've doesn't time learning Haskell.
Look at the thousands of people who learned Haskell and then also felt inspired to write a monad tutorial and publish it on the internet. Your search engine will help to see just how many there are.
Contrast with the number of useful applications you can actually install and run that are for some purpose that is not programming.
In fact there are more Haskell textbooks you can read than haskell programs you can install (excluding programs for programming - because that's some pyramid scheme vibe there).
This tells us something, I didn't even suggest what it tells us. What it suggests to you is what you thought of.
And fwiw imho pandoc is extremely useful! And Haskell is great fun and worth your time to learn!
I build everything with Yesod. I'm sure the others are good too; Servant seems great. But this is the one I started with, and it's served me very well for the past few years.
And how many programmers are paid to write anything anyone has heard of? And how many new well-known not-de elopement-related tools have been written in the last few years?
I thought that was the point of the section- that you may be able to tell something is wrong with the example, but the compiler can't. Thus by programming in that fashion you're not taking advantage of the power of the compiler.
The improved way below where the Just a is unwrapped with a case allows the compiler to see whether or not x is valid.
It's really aggravating that HN goes against it's own culture so thoroughly when it comes to Haskell. We're supposed to comment on what TFA says, not lick our wounds about how Haskell wasn't what we wanted it to be. f you can't contribute to the thrust of TFA how about creating space to listen to people who actually know what they're talking about?
Every single piece of hackneyed received wisdom and FUD in here can be easily countered if you talk to somebody with actual production Haskell experience. Speaking for myself:
- I run a startup that has production applications making money for 4 years now written entirely in Haskell with 6 devs doing nothing but Haskell (as well as engineers working in JS and our own language, Pact, yes, written in Haskell).
- When we need more bandwidth, we work with an all-Haskell consultancy that itself has no trouble finding work and is very successful in their own right.
- Before that, I built a group at a major bank writing Haskell code and getting it out in production, and outperforming Java apps.
- The tired "eww static types aren't for real business" is opinions masquerading as "facts". If you are a half-decent engineer in ANY language you can make your code refactorable for changing requirements. Whining that types makes that harder just shows your own limitations. Any decent programmer working in a strongly-typed system leverages types to make code _more_ refactorable in _less_ time with _fewer_ bugs. But hey these are just our anecdata too. Here's what I'm not doing: spreading FUD about Python or Clojure or JS, I'm too busy loving what I do.
- From a hiring perspective, Haskell programmers as a group offer an immense strategic advantage, and it's mainly _because_ they had to learn Haskell on the weekend and it wasn't handed to them. That shows passion and grit. I've built two teams from scratch now in totally different circumstances and it is simply breezy to find a wide variety of experience levels to craft a team from; they are by far the best teams I have ever worked with, with zero duds. The community is strong and excellent and helps each other out as far as job hunting goes.
Don't blame Haskell if it didn't stick, don't hate on Haskell if you find it intimidating or pointless. If anything, you should be grateful that there is a language that is actually different enough to attract a different kind of programmer, lord knows it's why I'm here: after having jobs in Java, C++, Perl, Ruby, JS, Visual Basic, Hypercard, you name it, it was nice to see that there's a different way to do things. It's really fun, there's always more to learn (not true of every language btw), and finally, it kicks serious ass on the performance side (as GC/runtime languages go).
What's up with the formatting on this page? In Firefox for iOS (same rendering engine as mobile Safari), all lines but the first one in code listings have 4-character indents, and lines use a larger-than-normal font size seemingly at random.
It's a pity I don't use Haskell at work and yet learning Haskell was the single most bang for buck exercise I have ever done. "Parallel and Concurrent Programming in Haskell" is the best resource I have ever read on parallel and concurrent programming concepts.
Every programmer should learn this language even if they never plan/get to use it.