Why Not Haskell?
I’m going to try writing a balanced post about a programming language. Meaning that the words suck, shit, moron and brain dead are prohibited. Enterprisey too. And scale.
I’ve been mucking around with Haskell for some time now, enough to know my monad transformers, functors and function composition. It’s all serious shit. Ooops, sorry. So I won’t lie to you: the learning curve is steeper than for most other languages I’ve learned before and I already have some decent functional programming skills. The purity of Haskell brings lots of advantages but it definitely requires more brain power than Java (random example). You also write at least twice less code than Java (another random example), in terms of terseness it’s close to Ruby and can arguably be more expressive.
Now don’t let the apparent complexity put you off, I’m just warning you a bit, you’ll have to stay focused to learn Haskell, but it’s still very reachable and most importantly (spoiler alert) it’s worth it.
The Community
Haskell enjoys a really nice, helpful community. That might explain their unofficial slogan (“avoid success at all cost”). The landscape would probably change a bit if the language became mainstream. The #haskell channel on freenode irc is pretty amazing for a relatively small community: it’s actually one of the largest around (they’ve beaten a record of some sort recently). You can pretty much always find a nice person to answer to your dumb questions, whatever your timezone.
Then there’s the Haskell Cafe mailing-list with similar qualities. The traffic is medium, trolling quasi inexistent and interesting subjects being discussed daily.
My only reserve in the community section (hey, I have to say at least one negative thing, right?) is that there’s still a very present academic stance. You’ll see a few conversations turn to category theory geekness. A lot of the libraries documentation is actually publication papers (pdf warnings). I don’t really find it annoying (except for the pdfs), it’s a nice change from the enterprisey (did it again) world. But YMMV.
Finally there’s a truly awesome book, Real World Haskell, to get started with the language. It may very well become an equivalent to the pickaxe in Ruby.
The Implementation(s)
Again, Haskell shines here. The compilers are really top-notch, nothing less. The reference is GHC but there are a few others and initiatives trying to out-GHC GHC. You can both compile code to an executable or have it interpreted, which makes experimentation much easier. The point is, there’s a strong ecosystem and most of it is production quality. No noodling here, just good software. Really.
And one of the sweetest thing I’ve found about Haskell is that the produced executables are pretty damn fast, especially for a high level language. To a point where you have to wonder why you would use anything else when you’re trying to optimize for performance. I can tell you, the Haskell experience beats hands down the C++ or Java experience.
The Language
Finally, I can fully indulge in my programming language geekery. So if you followed the first link I used for Haskell, you should know that it’s a pure functional language. It’s not its only important characteristic, far from it (personally I like partial application a lot), but it’s probably the one that had the biggest influence on the rest of the language. One of the consequences is that Haskell relies heavily on monads to model state (and not only that). Explaining monads is not the scope of this post, if you ask me they actually shouldn’t be explained, just experimented with. But the consequence is that Haskell is almost two languages in one. You have the “pure way” of programming and the “monadic way” of programming (and please, if this gives you a knee-jerk reaction, read on).
The “pure way” is like a honeymoon: it’s all beautiful, really. You have partial application, function composition, pattern matching, higher order functions, type inference and it’s all boundless. Once you get used to it, it’s like brain candy.
The monadic ways is more like 5 years after the honeymoon. Don’t get me wrong, I’ve been happily married for more than that. It’s just more an age of pragmatism. You have to build on top of all past decisions, work, lifestyle, … And you can’t always live in a honeymoon, there are other important stuff out there. Like hard disks, network and users (that’s where the metaphor breaks). So the “monadic style”, even though it works well is still terse and pretty sweet, just isn’t as beautiful.
Now you have to realize that my last two paragraphs were purposefully Manichean, I just wanted to give you a feel of the difference. Practically, it’s far from being so black and white. “monadic style” is more like an embedded DSL (it’s actually just a lot of syntax sugar), you can still do all the nice stuff like currying or composing monadic functions. Most importantly, it all feels like Haskell.
Writing a single section about the language is actually slightly painful, I feel it deserves much more. But that will be yours to discover. Believe me, you’ll enjoy the expressive power.
Conclusion
Haskell is now one of my favorite programming languages, sharing the spot with Ruby and Scheme (Javascript is close by). I’m not saying it will fit every purpose better than any other language, all programming languages have their respective sweet spot. But overall, I’m really damn happy to have Haskell with me in my toolbox.
Update: in the community section, I should probably add that Haskell has a very large ecosystem of libraries called Hackage. Hackage integrates a package manager (because you can never get enough of them) that makes installation rather straightforward. Now you know how to get a Haskell Tetris!
Image creds: martinlabar and arielarielariel.
Matthieu Riou on February 16th 2009 in Uncategorized
Barry responded on 17 Feb 2009 at 3:50 am #
It’s weird you say your favourite languages are haskell ruby and scheme as I’m working my way through The craft of functional programming contrasting those exact three languages as I go.
Why not Haskell? « FP@StAnd responded on 17 Feb 2009 at 7:30 am #
[...] http://offthelip.org/?p=125 [...]
I. J. Kennedy responded on 17 Feb 2009 at 7:46 am #
Nicely done, sir.
dons responded on 17 Feb 2009 at 8:19 am #
One thing you didn’t really mention – and which I think is significant (at least we’ve worked very hard on it) – is the Hackage + Cabal infrastructure, and the resulting 1k+ libraries for doing all sorts of things.
It’s hard to remember how we did Haskell programming before there were libraries for everything
Greg Graham responded on 17 Feb 2009 at 8:32 am #
Thank you for a balanced piece of language advocacy. It was refreshing to read. I’m learning Scheme right now, but I think Haskell will be next on my list.
mark responded on 17 Feb 2009 at 8:55 am #
Ok the article is great, but when I read “Javascript is close by”, I got confused… How can javascript reach Ruby or Haskell in any way other than browsers needed a crappy language to deal with some “advanced” logic, but it still fills only the browser ecosystem needs whereas both ruby and haskell expanded into MANY other areas?
Matthieu Riou responded on 17 Feb 2009 at 10:10 am #
@dons: You have a point and I can certainly appreciate the amount of work behind Hackage. But nowadays, most developers take extensive libraries for granted. So not having one would probably be a negative point
But you’re right I should have at least mentioned Hackage, I’ll add an update.
@mark: Thanks. I recognize it’s a bit weird. Take it this way: Javascript is a minimal language (a bit like scheme in that sense), it supports first class functions and closures and used with a decent supporting library (like Prototype or JQuery) it’s actually pretty decent, including higher order functions. It has quite a few quirks though.
Muri Porcara responded on 18 Feb 2009 at 2:16 am #
Haskell isn’t ready for real use. It’s mostly for language theorists and people who like languages more than actually creating anything.
And I have proof.
Haskell does not support the equivalent of structures. Haskell calls them “records”, and the language literally cannot even create a type which organizes “a collection of names” that are unique to that type.
I’m going to try to explain this better so you can see how amazingly pathetic the language actually is:
Haskell *literally* cannot make a thing called:
a type called
“Monster”
with a member called “x”
and a type called
“Gridunit”
with a member called “x”
…without introducing *global functions* into the global namespace.
This instantly destroys the language.
I wanted to use it for a work project, but my company has banned use of the language due to the record syntax making it near impossible to write large projects without significant and endless creation of “new files” for every tiny little thing.
With Haskell, you cannot even group source files together by usefulness and functional coherency to the entire app — instead you create individual source files because you’re forced to do so by language blunders. It lacks elegance on so many levels.
Because Haskell cannot even manage the *most basic essential everyday super common* programming idiom, it is downgraded from “useful” to “obscure crap you probably don’t want to mess too much with.”
Razee Marikar responded on 18 Feb 2009 at 4:41 am #
Hi,
Liked your random examples!!!
Matthieu Riou responded on 18 Feb 2009 at 10:51 am #
@Muri: I’m not sure I completely understand your example, could you provide a code snippet in your favorite language to see exactly what you mean? There are quite a few data types in Haskell (arrays, tuples, maps, …) and also quite a few primitive to define types (type, newtype, data, …).
Regarding modules, I recognize I’m not completely happy with the one-module-per-file policy. But it’s not really a show stopper for me, it’s still far better than Java as you can structure your modules pretty much the way you want, with as many functions as you need.
Muri Porcara responded on 18 Feb 2009 at 12:14 pm #
Mitthieu Riou
in C++, you can do this:
struct Animal { int x; float age; };
struct GridUnit { int x; int size; };
the equivalent in Haskell is illegal because both Animal and GridUnit have a member with the same name.
‘x’ is a global.
Aaron responded on 18 Feb 2009 at 2:30 pm #
It’s true that record support in haskell is pretty minimal.
The equivalent in Haskell is “data Animal = Animal { x :: Int; age :: Float }”, which desugars into data Animal = Animal Int Float and the two functions x :: Animal -> Int and age :: Animal -> Float, (as well as enabling some funky update syntax).
These functions aren’t truly global, but limited to the module in which you define them. Feel free to not import them elsewhere, or only import qualified. If even that is too much, you don’t have to use this feature. Records don’t buy you much, and you don’t have to use them.
Just create the one data structure with “data Animal = Animal Int Float”
If you don’t want those functions, don’t create them. Just create the algebraic data type without named fields. If you want make them an instance of class GetX a where x :: a -> Int which lets you share the accessor name with others.
Daniel responded on 18 Feb 2009 at 11:47 pm #
@Muri: you’re saying basically, “I can’t do X in language A the way I would in language B, so I’m not going to bother with language A.” Haskell uses pattern matching for situations like that, which are arguably more generally useful, so I’ll say back at you:
I can prove that your language is unsuitable for real applications! your language can’t write something as basic as:
function(0) = 1;
function(x) = x * (x – 1);
you have to resort to an ugly if statement to do something so fundamental! I will never touch any language without pattern matching for this reason!!
Of course that’s a gross oversimplification (as was in my opinion your analysis) as it misses the point that different languages approach problems in different ways. You might as well criticize haskell for not allowing mutable variables or global state. If you want to try haskell again with a more open attitude, feel free to drop by #haskell and we’d be happy to help you out
Sergey Shepelev responded on 19 Feb 2009 at 4:26 am #
Thank you very much, Matthieu.
Your “two languages in one: pure and monadic” helped me a lot to understand Haskell.
dmead responded on 19 Feb 2009 at 7:03 am #
Muri Porcara is completely wrong. What he’s really saying is that Haskell isn’t compatible with the way his company writes software which has to be traditionally modular.
Muri Porcara responded on 20 Feb 2009 at 4:23 am #
The question was, “Why not haskell?”, so I am truly and honestly answering that question. The record problem I mentioned is literally the reason I won’t use Haskell.
Aaron, I disagree with everything you said. You try to make it seem like it’s not a language-destroying issue.
It’s like saying “Gee, your car has no stick shift? No problem. Feel free to just stop at the corner of each block, get out and open the hood, stick your finger into the LZ-2 engine block, bend finger, and PULL UP (twice to gear up, once to gear down), close the hood, get back in the car, and you should be good to go.”
1) “Feel free to not import them elsewhere, or only import qualified.”
I definitely under NO CIRCUMSTANCE or any stretch of the mind feel even vaguely “free” to do that. What you said is absolutely horrible and it doesn’t solve the problem.
I don’t want to make 500 little files in a codebase just because of one ridiculous language flaw. I like to make nice functional modules which are organized by what I’m doing in the codebase.
document.cpp
pview.cpp / .h
peditorcontrol.cpp / .h
peditorinner.cpp / .h
player.cpp
etc.
Now a given .h might have various types defined inside it which are relevant particularly to the corresponding source file. For example, peditorcontrol.h has 21 types in it which are used *primarily* for peditorcontrol.cpp, but are used a few times elsewhere by other files, who merely import the .h.
Now of course, some of these types have clashing names, and what are you proposing I do in Haskell?
a) Sorry buddy, you can no longer organize your code into clean and simple units of functionality. You can no longer group types related to “editorinner” into the editorinner source file. You’re going to need to create 21 new files and put every little local type into a new “Module”
b) Sorry buddy, you’re no longer going to be able to do something as simple as look at a page of text with 15 types on it, and read them all clearly in one place. This is a very basic idea of programming. Looking at some types in a source file. Nope, maybe if you get some very intense VIM or Emacs scripts you’ll be able to wire up a system that loads all your files in little vertical panes in the editor… Wow…
c) MAYBE you can avoid point a) if you only create “new modules” when there’s specifically a conflict. So now you’ve got this pathetically inconsistent mish-mash of types in the relevant source file, and then only certain types (FOR NO GOOD REASON OTHER THAN A LANGUAGE FLAW) being on the filesystem.
d) Sorry buddy, you can no longer write a useful little type right near the callsite and use it once or twice. It’s probably going to conflict with something. Or maybe it won’t, but you can never be too sure.
And now…
2) “If you don’t want those functions, don’t create them. Just create the algebraic data type without named fields.”
Oh please. It’s like saying “Gosh, if you want to have a nice dinner, why don’t you just eat dog food?”
You have very, very low standards. You think we’re just supposed to be all happy and giggly that the language can’t even support named fields properly so we just go and use unnamed fields FOR NO OTHER GOOD REASON THAN A LANGUAGE DEFICIENCY. That is key. Can you see that there’s no reason I’d want the unnamed fields besides haskell problems?
And the unnamed fields are very difficult to remember. I can remember “age”, much easier than I can remember _ _ _ _ _ _ _ x _ _ _. You no longer have a mnemonic queue and instead have to reference the source code to figure out which field number it is, and count it in your head.
————————–
You’ve got to take a step back and be subjective here. Imagine if Microsoft announced that the new F# couldn’t do records, or that the new C# couldn’t do it… or if GNU announced that the new C++ wasn’t going to allow members of structs to have the same member name unless they were in different translation units.
Do you really think anyone would use that crap? You are vastly underestimating the importance of getting these fundamental basics right. Yes, real people, regular people, they want to be able to not have to remember unnamed field indexes.
This stuff is REQUIRED to get past “nerdy experimenter language”
————————–
The flaw also limits the scope of what Haskell can even be used for. Haskell is a language that is not suited for any kind of big project. In big projects there can be MANY types. And in the projects I’ve seen so far, this is kept sane by grouping types together into larger files by programmatic functionality / module (the REAL sense of module), and NOT by language deficiency. Haskell modules mean language deficiency, whereas modules should really mean functionality. It totally defeats the purpose of modules. You can’t even group stuff together.
Imagine if MS Windows was coded in Haskell, and there was 100,000 little modules for every tiny little type.
————————–
Haskell is dead to me, the whole language is completely over.
Matthieu Riou responded on 20 Feb 2009 at 4:04 pm #
@Muri: please try to stay correct in your comments, I accepted it for the sake of objectivity but I’d rather avoid a flame war.
I tend to agree with Daniel here. There are actually quite a few large projects that have been built in Haskell, to my knowledge without experiencing the fatal flaws you’re mentioning.
I respect your opinion though, but you’ll have troubles finding another language you like after C++
Curt Sampson responded on 21 Feb 2009 at 5:28 am #
@Muri: When I started with Haskell just under a year ago, I was quite worried about that exact same issue. I was worried even further about other namespace control issues.
Almost a year later, I’ve got a reasonably large application: an automated options trading system consisting of 51 modules, 201 defined types (176 of them data, 24 of them newtype), and about 6000 lines of code. Don’t let the small numbers fool you; this replaces more than 50,000 lines of Java code: Haskell is quite terse. (As an example, my parser for market data feed messages is about 450 lines of Haskell. Java couldn’t get anywhere near that.)
In the end, the namespace issues turned out not to be such a problem. More interestingly, some of those issues are there for language design reasons that make the language better, though I won’t get into the details here.
When learning a new langage that’s sufficiently different from what you’re used to, you’ll almost always find something that seems idiotic, from your current point of view. Generally, if you just accept it and move on with learning, you’ll find that it was your mindset being stuck in a different language that created the issue, and it wasn’t a problem with the language itself.
So I’d suggest you set your one little thing aside, give Haskell a serious try with a non-trivial application, and see what you think afterwards. I expect that your mind will change just as mine did.
cjs@cynic.net
Eyal Lotem responded on 22 Feb 2009 at 1:47 pm #
Moreover, the strategy Muri is suggesting is very destructive for type inference.
Consider the code:
int f(var blah) {
return blah.x;
}
How do you infer the type of f (specifically, the type of its argument “blah”)?
The globally unique names record fields have the important advantage of making the type clear for the type inference engine. If I’m not mistaken, this is one of the roadblocks holding C# global type inference from taking place.
Jonathan responded on 27 Feb 2009 at 12:20 pm #
Only you could compare writing code to a honeymoon!
QCon London 2009 - A huge breath of air before a long apnea in the IT real world « The Nameless One responded on 22 Mar 2009 at 1:22 pm #
[...] World Haskell book in my bag (Yeah I know, Erlang is cool and was well covered during QCon but a friend advised me to have a look at Haskell). At the end, I learnt many concepts and ideas that could be [...]
missy-film responded on 24 Mar 2009 at 2:16 pm #
Nicely sorted, helped me understand haskell
Ebrahim responded on 07 Apr 2009 at 2:33 pm #
Thanks
I’m now totally convinced that I must learn Haskell.
Curt Sampson responded on 01 May 2009 at 10:35 pm #
Doh! As it turns out, Muri’s complaint was addressed at least two years ago, anyway, with a language extension that’s available in GHC now and will doubtless be part of the next Haskell standard: record field disambiguation:
http://www.haskell.org/ghc/docs/latest/html/users_guide/syntax-extns.html#disambiguate-fields
cjs@cynic.net
Dan responded on 12 Aug 2009 at 6:41 am #
@Curt – One thing I do worry about though, is the amount of functionality that’s not actually “language standard” vs. “nice GHC extension.” In other words – the language standard seems to be “whatever the latest GHC supports.”
Does this make anybody else nervous?
Curt Sampson responded on 09 Sep 2009 at 6:24 am #
No, because GHC is the most common implementation testbed for most of the features going in to Haskell’ (pronounced “HASK-ell-PRIME”), the next version of the standard. For most of us using GHC extensions, we simply stop declaring that we’re using them when we start using Haskell’.
cjs@cynic.net
antage responded on 11 Jan 2010 at 9:29 pm #
@Curt, -XDisambiguateRecordFields does not solve Muri’s problem.
rectest.hs:
module Main where
data Rec1 = Rec1 { a :: Int, b :: Int }
data Rec2 = Rec2 { a :: Int, c :: Int }
$ ghc -c -XDisambiguateRecordFields rectest.hs
rectest.hs:4:19:
Multiple declarations of `Main.a’
Declared at: rectest.hs:3:19
rectest.hs:4:19
dp responded on 18 Jun 2010 at 8:18 pm #
here’s something that bothers me about haskell: data constructors appear to be functions but aren’t, in any traditional sense. example:
data NaturalNumber = NaturalNumber Integer
there isn’t anything restricting NaturalNumber to be greater or equal to zero. data constructors are functions you don’t define, which makes sense because if you did define them i’m not really sure where it all would end… but it’s still annoying that you can’t make any kind of assertions on the data that is given to your data constructors.
another thing, why isn’t there function type polymorphism. that is, i can’t define an operator -> because it’s taken, even if the type would be completely different. same goes for map. if you could do those things, making new operators wouldn’t be such a pain because you’d have better options.
CD-Host responded on 10 Sep 2011 at 10:26 am #
This is an old thread I hit while googling for something else. But just in case anyone else hits this, Muri’s point is frankly silly.
In general the base structure for Haskell is that “variables” aren’t named in type definition but by the function that uses them. The record syntax does the opposite when the programmer wants a global explicit name. So Muri’s comment basically boiils down to “I don’t like the fact that the global variable naming conventions default to being global, and that to make them local I have to jump through hoops”.
Of course the obvious answer is don’t use a global naming structure if you don’t want global names.
The standard for a simple struct like what one does is C is just to create an alias which:
type Vertex = (Float, Float)
And then in a function which takes a vertex which applies a name
f :: Vertex -> Integer
f (x,y) = round (x+y)
and that name is local to the definition of f.
And this was Aaron’s point, the record syntax is a “sugaring” of the creation of a structure with global scoped function. You simply don’t create global functions if you don’t want them. If you do want x and y to have meaningful names then those names are truly global.
Haskell does not allow for something like BASIC’s idea that you can have
A — the float A
$A — the string A
%A — the integer A
If A is global it is universal and only one of these. But even if you did want all the possibility for all three that’s not hard. You just A as
A:: (Maybe Float, Maybe String, Maybe Integer)
____
Haskell doesn’t support doing things the Algol way. It does support doing anything you would want to do Algol like language.
CD-Host responded on 10 Sep 2011 at 10:34 am #
dp
You can make new operators and checks.
for example:
data NaturalNumber = NaturalNumber Integer
newNN :: Integer -> Maybe NaturalNumber
newNN i = if i > 0
then Just (NaturalNumber i)
else Nothing
So write you program for natural numbers and lift the whole program to the Maybe NaturalNumber monad when you need to take Integer input. The compiler needs to know what to do with negative numbers and sending them to Nothing is reasonable choice.
EvgenijM86 responded on 20 Sep 2011 at 3:24 pm #
>>Doh! As it turns out, Muri’s complaint was addressed at least two years ago, anyway, with a language extension that’s available in GHC now and will doubtless be part of the next Haskell standard: record field disambiguation:<<
No, as turns it out, even with "record field disambiguation" you still have to place types with identical named fields in to different modules. If you try to do something like this in a single module:
data S = S {x :: Int}
data M = M {x :: Int}
it will simply not compile.
Jeanne-Kamikaze responded on 27 Jul 2012 at 8:41 am #
@Muri
“I don’t want to make 500 little files in a codebase just because of one ridiculous language flaw.”
However you seem pretty comfortable maintaining duplicate code in .h and .cpp files.
Alexey Tarasevich responded on 05 Aug 2012 at 1:25 am #
I see here 2 unbalanced opinions regarding duplicated names of fields/methods for different data structures. On one side it’s Muri, on other side are people like CD-Host.
To @CD-Host i’d say 1. It’s rude and impolite to say “Muri’s point is frankly silly”. 2. You are missing the point if you don’t see a problem here. Natural language (like English) use same words to refer to different enough phenomenons and concepts. Those concepts may be somewhat related (that’s why they share the name), but still different. Then we naturally borrow those names from English to our program in some PL. We borrow (ambiguous) words from natural language because we need to remember them somehow and we want other human being understand what we’ve written. And then we have a potential conflict. The question is: does the PL provide tools to resolve the conflict gracefully, or does it get in the way. I don’t know Haskell that well, but I see that in this particular case Haskell get in the way. E.g. we might want length of String to return Integer and length of Vehicle to return (Float, Unit) pair. So I don’t see how type classes can easily resolve the problem.
Summarizing: 1) it’s valuable feature of a programming language to borrow ambiguous names from natural language and provide some means to resolve ambiguity 2) Haskell is not that good in this respect as typicall OOP languages.
On the other hand, and that’s what @Muri misses: any language has advantages and disadvantages. In any language you have to work around some limitations. E.g. in C++ you have those duplication between h & cpp files, you have to manually manage memory and so on. And what the article author says is that benefits of Haskell overweight the limitations.