Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Why learning Haskell/Python makes you a worse programmer (2006) (lukeplant.me.uk)
59 points by mrkgnao on April 15, 2016 | hide | past | favorite | 60 comments


I know exactly how he feels. This is basically the experience I had when I moved from Python to functional languages and it finally "clicked" how much more productive I could be in a Lisp or an ML.

Where I differ somewhat is that for me, it saved programming as I knew it. I wouldn't be a programmer right now if it weren't for discovering Lisps and MLs.

I fecking hated writing spaghetti OOP and tedious imperative loops. Mainstream programming languages are built on the lowest common denominator and the result is that as a younger person looking at the possibility of getting into computers as a career, I was scared off by the amount of willful tedium that seemed to define professional coding at the time.

Today I work in a Clojure shop, and if I didn't, I'd be very wary of looking for employers that expected me to use inferior tools simply because they did not understand more efficient functional ones.

It reminds me of some of the terrible kitchens I used to work at where I'd be expected to do things like cut meat with a cheap paring knife because the employer felt that a real chef's knife was "unnecessary" or "too expensive". I quit those jobs, and frankly, I wonder at programmers who don't have the same respect for their craft when it comes to programming tools.


The smugness of Haskell evangelists is what keeps me from using it.

I swear, the first rule of Haskell is to never shut the fuck up about Haskell.


I never wanted to be like that, but it wears you down constantly seeing people complaining about problems that are trivial, or going gaga over some "new" feature in a trendy language that's a trivial special case of something Haskell has had for 20 years. You get tired of telling people the same thing every day and it never gets any better. Eventually you start just treating the superiority of Haskell as fact, because it's too tiresome to be constantly pretending that other options have their merits when you know they don't.


Actually, much better approach is to shut the fsck up and write something useful. Funny how Erlang crowd doesn't show that much of smugness when it comes to parallel/concurrent programming. I've only seen two Haskell programs that were at least remotely useful to me (one being Pandoc), and then again, those programs had awful size of the binary and atrocious memory requirements.


>Actually, much better approach is to shut the fsck up and write something useful

No, it really isn't. What is the goal there? To prove you can use a general purpose language to write software? There's tons of software already that proves that. How many haskell projects need to exist before it has proven itself or whatever it is you are expecting from this? If I write enough haskell software, the industry will just suddenly stop using bad languages to write bad software and poof care about quality miraculously? How is the industry supposed to know I have written this haskell software if I am not allowed to talk about haskell, or writing software in haskell, or how good haskell was for writing software when I used it? And why is haskell the only language where nobody is allowed to talk about it or they are variously branded smug, arrogant, academic blowhards, etc, etc?


I do (well Scala rather than Haskell); I write useful programs and am paid well for it. But it's still frustrating to see the sheer wastefulness of so much of the wider industry.


The thing that makes me skeptical of this view is the relative dearth of software written in haskell.

I can point to a hundred things I use every day developed with python (package manager, reddit, instagram, etc.) but the only thing I can think of that was built in haskell is Facebook's spam filter.


You wouldn't necessarily notice. A lot of the use of functional languages is finance-industry where companies prefer to keep the technology they're using secret.


I feel like this argument comes up whenever a high-level language with mysteriously low uptake is under discussion. Lisp, APL/J/K, Haskell - all are "used in finance, but it's secret sauce so you don't hear about it".

This makes me uncomfortable. Use in a particular niche sector does not preclude use in a different sector. Finance may well secretly use all of these weird high-level languages (though how would you falsify this?), but that still doesn't explain why you don't see them anywhere else.

So either finance is amazingly brave or well-suited to niche functional languages (in which case use in finance proves nothing), or it's a conveniently reputable-sounding use that doesn't actually require any proof. I, for one, will continue treating visible, published software (and libraries) as a useful metric of programming language usefulness and popularity.


> So either finance is amazingly brave or well-suited to niche functional languages (in which case use in finance proves nothing), or it's a conveniently reputable-sounding use that doesn't actually require any proof. I, for one, will continue treating visible, published software (and libraries) as a useful metric of programming language usefulness and popularity.

I'd say it's the opposite: use in visible, published software and libraries is a very specific niche (or rather two specific niches: OSS and webdev), and not representative of software as a whole. The open-source community is a Unix- and therefore C-oriented community; a lot of Haskell work happens at Microsoft, and a lot of JVM work happens at Oracle, and a lot of OCaml work happens in Europe, which puts all those things somewhat at odds with RedHat. Webdev is uniquely ill-suited to typed languages because of the history of browsers and HTML in particular. If you just looked at published software/libraries you'd think no-one was using Java or C++, when those are likely still the most popular languages in software development as a whole.


>use in visible, published software and libraries is a very specific niche

facepalm


The Haskell community could silence the critics quickly by pointing to some real world open source applications which prove the practical usability of Haskell. So far there aren't much. Most of them deal with DSL. There are only two Haskell IDEs written in Haskell: Leksah and Yi. The only good native IDE (AFAIK) is EclipseFP, and that is written in Java.

Perhaps some Haskellers should take the effort and develop a really sophisticated Haskell IDE which Haskell beginners could follow and use to learn and advance in Haskell. Theoretic knowledge about Haskell is good but practical application is another thing.


Please, not another IDE. Perhaps a compiler or a static analyzer -- the task Haskell is a very good fit for.


Which is all very well except you wouldn't necessarily notice most of what is written in most languages.


I too like to punish myself in a futile attempt to spite people who don't even know I exist, much less that I am trying to spite them. That'll teach them to like something!


You might want to look into OCaml. The language is not as nice as Haskell, but they also have less zealots.


That may be just people near you, physically or online. The Haskell community is really friendly and open.

I'll admit the motivation to talk about Haskell just as an indirect way to complain about how bad other languages are.


I feel the author's pain. I had a short consulting gig recently where I was asked to speed up a simulation job written in Python by a mechanical engineer. Smart guy but not a programmer, so the script was full of global variables, side effects and imperative loops.

I parallelised the script, which is pretty easy in Python but involved refactoring into a more pure functional style. I abstracted out the algorithms and added in some persistent memoization as a bonus. It was clean and did what they needed, but... unfortunately was now completely unintelligible to the engineer who wrote it.

When I was asked back 6 months later to help again, they had reverted to using and modifying the old script. This time I kept the changes to the absolute minimum.


You lost the opportunity for recurring jobs every six months.


I know this is 10 years old, So the more interesting thing to me is that these days you can do this functional style in most languages, even Java!

    foo.stream().filter(s -> !s.isEmpty()).collect(Collectors.joining("\n"));
So I'd say it didn't make them a worse programmer, it's just that the languages were/are behind.

Edit: Yup, forgot to map the description, but I'll leave it now.


You forgot the map operation (and I would also filter out nulls): (just to be pedantic)

  foo.stream()
     .filter(s -> s != null)
     .map(s -> s.description())
     .filter(s -> !s.isEmpty())
     .collect(Collectors.joining("\n"));


or in C#:

    string.Join("\n", foo.Select(x => x.Description()).Where(x => x != ""))


Where it does make you a worse programmer however, is when you don't realize that using the join method on an iterator means that you are going to concatenate a whole lot of strings, while in most cases, you actually could know the size of the final string from the beginning, and you don't need to allocate a whole bunch of arrays in the process.

These idioms have a complete disregard towards performance.


Many operations are optimized for typical use cases, for instance calling C# `string.Join` on short iterators will allocate no memory beyond that of the final string. The definition of "short" may be obscure (result is less than ~350 characters), but the optimization exists and is documented for anyone who cares enough about the tools in their toolbox.

I do agree with your general point, though. I remember a one-line change in a Haskell function that led to a 10x increase in performance:

https://news.ycombinator.com/item?id=6912474


If you don't profile your code automatically on every build then you have no business even thinking about such things. And if you do, that will alert you as to whether it's a problem or not. Let the computer do the bookkeeping for you, that's what being a programmer is.


I am simply saying that automatically choosing the nicer looking code just because it's nicer is not always the proper option. Being a good developer implies knowing when you're making a readability / performance tradeoff. If using idioms makes you lazy to the point that you no longer want to write more efficient code, then yes, it's making you a worse developer.


Laziness is one of the three cardinal virtues of a programmer. In my experience thinking you know the performance characteristics of any given piece of code is always a mistake. It's certainly not true that functional idioms in general perform worse - e.g. map/reduce style can be automatically parallelized, making it more efficient on modern multicore machines than explicit loops. If you know you're writing for a specific computer you can no doubt do better by using assembly, but functional code in general is more adaptable to the unknown future, because it tends to require specifying less of the details.


Look, I am not here to say that functional programming is bad, I really enjoy using these idioms. I am just speaking from experience. Since Linq has come to C#, I see things like this in my workplace:

    VeryLargeList.Select(x => x.Blah).ToArray()
And people are wondering why they get OutOfMemoryException.

Before linq, people would just loop, and the array would not have to be allocated dynamically.

All I am saying is "more abstraction may lead to performace issue, and people should be aware of it"


There's no reason that should use any more memory than an explicit loop. Maybe the particular implementation in C# does, but that's an implementation problem, not a language problem.


That's my point: the language cleanliness may obfuscate dirty implementation details, and a good developer should know what these are.

Another example, when you do this:

    SuperLargeList.Where(x => x.Blah).ToArray()
There is no way for the compiler to know whether the output array will have a small size or a large size, and so it will choose one or the other assumption (dynamic vs fixed allocation). The language chooses for you how it implements things, and this is all fine as long as you know about it, and as long as you can do something about it in the case when you need to optimize.


> That's my point: the language cleanliness may obfuscate dirty implementation details, and a good developer should know what these are.

So what, we should write everything as CPU microops? A good developer should be capable of looking at that level, sure, but it shouldn't be the default; most of the time the computer is better at judging these questions than the developer.


map can only be automatically parallelized if the function has no side effects. Most languages are incapable of identifying if this is the case, so the compilers can not automatically parallelize such code.


I strongly disagree. Automatic profiling only helps when you have a high-performance module and you want to prevent random commits from hurting that performance.

If you write a module with no regard for performance, because automatic profiling is still "in the green", you will spend your entire time budget (milliseconds-per-frame, milliseconds-per-request, milliseconds-per-megabyte, etc.) in the first half of your project and slowly realize that it's getting harder and harder to add new features without going into orange or even red territory. Sure, you can shave off 10% or 20% in "quick wins" with a bit of profiling, but nothing short of a rewrite can save you from inefficiency by a thousand cuts.

This doesn't matter if the module is not performance-critical, obviously. But when it is, then you should either plan for performance from the very start, or plan for a rewrite.


> These idioms have a complete disregard towards performance.

As they should. "Premature optimization is the root of all evil".


What I am pointing out is that by his own admission, the OP is likely to _never_ think about writing the code in a more efficient manner. He has _no_ _idea_ of the difference. Premature optimization may very well be the root of all evil, but knowingly ignoring the physical constraints of a computer is not great either.


The actual quote is "premature emphasis on efficiency is a big mistake which may well be the source of most programming complexity and grief."

The 'summarized' catch-phrase always bothers me; the choice between optimized code and inefficient code is always clear to me.


The "premature optimization" quote is from Knuth's Turing Award lecture:

http://delivery.acm.org/10.1145/370000/361612/a1974-knuth.pd...

"The real problem is that programmers have spent far too much time worrying about efficiency in the wrong places and at the wrong times; premature optimization is the root of all evil (or at least most of it) in programming."


..as well as "Structured Programming with go to Statements".


> [M]y experience with Haskell means that I now see potential bugs everywhere in imperative code. [...] Now I know there are other ways of doing these things, I find it difficult to be satisfied with any of the code I write. I am constantly aware that I am writing traps that other people are likely to fall in.

Good. That's exactly what it was supposed to do to your mind. Now you need to learn to live with this awareness, and you need to learn how to minimize the amount of traps you leave behind.

> I also find C# code very ugly compared to both Python and Haskell. On the visual level, the mandatory use of braces everywhere [...] makes code contain a lot of line noise and empty space, and combined with the verbosity of the libraries and type declarations etc, you find that a page of C# hardly does anything.

Learn two or three more languages and you'll find that this visual clutter hardly matters. What's important is how much can you do with the same amount of statements and expressions (I find Paul Graham's proposition that this is related to the count of AST nodes appealing).

> 2. Using functional style obfuscates your code when using other languages.

This sounds like the really old programming joke: "good programmer can write in Fortran in any language".

Some constructs are not a good fit for some languages. I avoid map(), filter(), and reduce() in Python like the plague, but I find list comprehensions appropriate. You need to learn to switch between paradigms and sets of idioms, not to cram the same set everywhere.


> > I also find C# code very ugly compared to both Python and Haskell. On the visual level, the mandatory use of braces everywhere [...] makes code contain a lot of line noise and empty space, and combined with the verbosity of the libraries and type declarations etc, you find that a page of C# hardly does anything. > Learn two or three more languages and you'll find that this visual clutter hardly matters. What's important is how much can you do with the same amount of statements and expressions (I find Paul Graham's proposition that this is related to the count of AST nodes appealing).

I would agree that if you follow the official Microsoft C# style conventions, you end up with some ugly looking, noisy code. I am not a fan of sticking opening braces on their own line, or forcing a newline between closing braces and else, catch, finally keywords. It is just a waste of too much vertical space, for questionable benefit.


By now you can do better in c#. You can do the following, for instance:

    mylist.Select(item => item.description)
          .Where(descr => descr != "")
          .Aggregate(a,b => a + "\n" +  b)
Edit: I guess those features weren't available at the time of this article? Haven't used c# for long enough to be sure.


Correct, in 2006 there were no LINQ, extension methods and a number of other great improvements which kind of makes the article less valuable.

Not that it was terribly valuable to begin with: as a programmer, using the language at hand to write code that 1) works and 2) is as clear as possible is exactly your job.

Emulating other languages because of feature envy is really stupid, the first C# snippet is a prime example of this. I would not want to work with someone who deliberately makes the code harder to read in order to mimic his/her own favorite language. Every language has its idioms, just learn them and quit bitching.

As a side note, the example chosen was particularly poor because, if the collection contains a huge number of items, concatenating them without a StringBuilder in .NET land is a terribile idea.


I don't know how it is with you, but I think I have learned a lot by learning Haskell - and I think it has made me a better programmer. I have to say, I am in no way fluent in Haskell (anymore), and I prefer erlang as the functional PL of my choice (better libraries, more open community).

I had a series of courses about concurrent programming at the University, with a Focus on programming in Haskell. Learning Haskell has taught me 2 things, and I am immenseley gratful that it did:

* The power of abstractions

* The power of types.

The second point is absoluteley incredible in Haskell. With proper reasoning about your datatypes, the implementation almost becomes an afterthought. I never had that insight when I was using Java, C or another statically typed language.

Also: I think if this aricle would be written today, the authour wouldn't be so frustrated. Many functional concepts have influenced other languages in the last 10 years (especially C#).


Haskell may not be the best in production, but it's amazing for learning computer science. It's also pretty good for learning quite a bit of intermediate and advanced programming. The concepts and styles of thinking it teaches can be applied elsewhere to great effect.


It's like why becoming fluent in a second speaking language makes you a 'worse' speaker in your native tongue. You sometimes confuse idioms and terms, and sometimes you find yourself frustrated when trying to find a word in one language, when the perfect word exists in the other.

(Tongue-in-cheek, if not apparent)


> Further, my experience with Haskell means that I now see potential bugs everywhere in imperative code. ... I am constantly aware that I am writing traps that other people are likely to fall in.

Did that not make him a _better_ programmer? Did he actually mean "Why learning Haskell/Python makes you a worse _feeling_ programmer"?


Think that's tough? Try learning formal methods and how to design software to be correct by construction. Haskell would be an easy sell. But regardless of language few seem capable of catching errors in logic -- the kind that lead to race-conditions, starvation, dog-piling, cache misses, consistency errors, etc.

The kinds of errors whose frequency and impact we are ill suited to estimate. "Bah, that could only happen for 1 in 100k requests." Now your system is serving 3M requests a second. It's very likely you'll be seeing that error.

Don't be discouraged though. You might read about a new distributed lock in an important open source cloud orchestration framework that the public is building service infrastructure on and not find a single executable specification. Doesn't mean you can't write one and submit a pull request when you inevitably find errors in it. You just have to keep such alien super powers under wraps.

Similarly if your goal is to write more Haskell at your job: just find opportunities to use it. Preferably ones where you can keep the secret of the sausage to yourself. When your bosses and colleagues begin to wonder how you consistently kick butt you can tell them. Then they might want to learn too.

At least that's the story I tell myself. Most people are happy in their own instance on Plato's cave. If you bring in new ideas you might be ousted from the group. Or fired.

Just keep doing the best mediocre job you can and try not to think about it too hard!


Haskell and Python are two languages that couldn't be more different. Are there developers that prefer both and constantly switch between them? Besides the obvious that Python has one or two cool libraries, why would anybody willingly do that?


> Are there developers that prefer both and constantly switch between them?

Yes. I do. I regularly use about half-a-dozen different programming languages, with C, Python and Haskell being my weapons of choice (when I have a choice).

I use Python for quick hacks where performance doesn't matter. I could do with Lua, Ruby or Perl, but I prefer Python because I know it and it's "batteries included".

I've used Haskell a lot, and I use it for more complex tasks such as parsing or compiler projects. Back when I was at the Uni, I really aced some courses because I could do programming project works really fast, while relying on the static typing and some ad-hoc testing with QuickCheck.

And I use C because there's no alternative for it in my day job (technically, Rust could do it now, but there are non-technical issues that prevent it so far). I also use it for some performance intensive scientific/math stuff as well as computer graphics.


Hello, I'm that guy who voluntarily writes a mixture of Python and Haskell in his day job.

For me, it's about recognizing the strengths in both languages. You mentioned Python having "one or two cool libraries", but that's quite an understatement if you're doing any sort of numerical work. NumPy and SciPy are incredibly powerful, fast, and consistent. While hMatrix and Accelerate are both wonderful packages and have made some great progress, they don't have nearly the community support that Numpy does.

On the reverse side, when I'm dealing with any sort of interactive system (e.g. Web Scraper or Motor Controller), I immediately reach for Haskell, since it makes those kinds of applications so much easier.

For me it's mostly about just finding the right tool for the job. And trying to avoid any job where the right tool is Fortran.


> On the reverse side, when I'm dealing with any sort of interactive system (e.g. Web Scraper or Motor Controller), I immediately reach for Haskell, since it makes those kinds of applications so much easier.

I find this a bit surprising... can you tell a bit more about these projects?

I tend to do the opposite, I reach for Python when I need interactive stuff or need to interface with the "real world" more (network connections, etc). And I use Haskell when I have projects that have well defined inputs and outputs.


They both allow you to naturally express idiomatic-functional code (in their own ways) in a way that you can't (or couldn't until recently) in C#/Java/etc. The Python and Haskell versions of the method example from the post look much more similar to each other than either does to the C# version.

There was a time when I wrote Python and ML and switched between them. Python had good library support and was slightly faster to write (I was fearless back then, but it seemed to work out ok), particularly for basic CRUD-type tasks. For cases of more involved business logic (really, any business logic at all) I needed higher correctness guarantees and would switch to ML. It worked well enough.


Although people may be reluctant to admit it, I suspect a large part of the 'similarity' is significant whitespace and the lack of ceremony when defining functions.


The comparisons with c# in this article is 10 years out date, so they are no longer relevant.


I finally have an excuse.


I do actually experience this : functional style is shorter, more effective, and more bugfree, and you can do it even in Go (if you're willing to copy bits to compensate for lack of generics).

However, there is a large class of programmers that will kill you on the code review if they see this. Mostly I think it's for lack of understanding, or I've gotten comments complaining exactly that it's not imperative code.

Unless you're willing to educate your colleagues, the post may have a point : learning functional style will improve your programs but seriously increase your frustrations.


The post sounds like an instance of solving the wrong problem. If you can write more concise, elegant and bug-free code, but all your colleagues can't deal with it and want to stick with their stuff, you're probably in the wrong place.

On the other hand: Python doesn't exactly impress me as a particularly "functional" language. The examples are not as straightforward as the Haskell examples. Python uses a weird mix of OOP, imperative and functional programming, and this creates mental strain for everybody who has to read it. When I switch from Ruby to Python, I usually enjoy the simple imperative-pseudo-code like way of how the most idiomatic code there works. Then when I go back to Ruby I'm happy to use idiomatic metaprogramming, chained blocks of code etc.

The same is true for Go and probably part of the current fascination of the dev community with the language (because it's proud of constraining complexity of expression).


> Python uses a weird mix of OOP, imperative and functional programming, and this creates mental strain for everybody who has to read it.

I used to think so until I came across Oz. It's Prolog, Lisp and Python all in one nice package :) Granted, it's more of an educational language, still though, Oz is a mind-stretcher.


> and this creates mental strain for everybody who has to read it.

Really? I find it just the opposite. List comprehensions are broadly welcomed instead of one- or two-line for loops, lambdas are quite useful in the right context (such as sorting, list comprehensions, etc), and functions are a first class citizen, and easily used (and recognized) as such.

It just lets me use the right tool for the job, without having to switch languages. And since the functional bits have been baked in for so long, it's easy to do.

And let's be fair, you can't say that there's cognitive overhead to functional programming in Python while claiming Ruby does not. Both languages have nearly identical capabilities in this regard (though, I do miss Ruby's multi-expresssion lambdas when in Python).


Well, "writing traps that other people are likely to fall in" does sound bad. I'm not sure I understand why that would be made excusable due to... knowing a couple other languages and that they exist?




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

Search: