Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Why Learning Haskell/Python Makes You a Worse Programmer (lukeplant.me.uk)
66 points by antiform on Dec 23, 2008 | hide | past | favorite | 50 comments


The author's C# example predated the release of LINQ, which makes the C# syntax much more like the Python's. The author's code:

string.Join("\n", mylist.ConvertAll<string>( delegate(Foo foo) { return foo.Description(); }).FindAll( delegate(string x) { return x != ""; }).ToArray());

can be rewritten as:

string.Join("\n", (from f in myList where f.Description() != "" select f.Description()).ToArray());

which is super-easy to read and understand. Maybe not as concise or pretty as Python or Haskell, but definitely a step in the right direction.

Others' comments that the author should consider F# are well founded, as it integrates seamlessly with other .NET-based code (such as C#) and is a pretty great language. However, it's worth noting that the post was written in 2006, well before F# was widely known about or distributed.


Perl:

   join "\n", grep { $_ } map { $_->description } @myList;
Or

   join "\n", grep /./, map { $_->description } @myList;
Ruby:

   myList.collect { |f| f.description }.select { |d| d != "" }.join("\n")
Python:

   descriptions = (f.description() for f in mylist)
Or if we'd like to constrain ourselves to one method `FooClass.description()`

   descriptions = map(FooClass.description, mylist) # `map` returns an iterator in py3k

   "\n".join(filter(None, descriptions))   # `None` means `lambda x: x` here
Or if you don't like `filter()`

   "\n".join(d for d in descriptions if d) 
Or if `description()` is a side-effect-free method then It should be a property:

   "\n".join(f.description for f in mylist if f.description)
At the end of the day new features doesn't matter due to most .NET-shops use old C# versions and It will not change any time soon.


  # `None` means `lambda x: x` here
I find it more helpful to think of it as meaning `bool` in that context. I think the None special case is only there because filter() predates bool() in Python.


Thanks. `bool` is more readable.

  "\n".join(filter(bool, descriptions))
Or `len`

  "\n".join(filter(len, descriptions))


Which seems the most "readable"? I think the Ruby one might be.


I like the python version: it is the densest, and most similar to set notation. It is done similarly in Fortress.


How about this?

    myList collect(description) select(!="") join("\n")
http://www.iolanguage.com/scm/git/checkout/Io/docs/IoGuide.h...


I like the Ruby one most. But "readable" is matter of taste (or programmer's background: I consider

  while (*to++ = *from++); 
to be a readable C code (not that I'll recommend it)).

It is wrong to judge a language by tiniest code examples.


Using Symbol#to_proc, the old Ruby

  myList.collect { |f| f.description }.select { |d| d != "" }.join("\n")
becomes

  myList.map(&:description).reject(&:empty?).join("\n")
which is even more direct: take my list, map each element to its description, reject anything empty, and join with newlines.


I thought `Symbol#to_proc` is a Rails' extension.


I learned about it in 2005, when I read it on http://pragdave.pragprog.com/pragdave/2005/11/symbolto_proc.... and it's such a good idea that they've folded it into 1.9 http://www.infoq.com/news/2008/02/to_proc-currying-ruby19


Python-like F# version:

    String.Join("\n", Array.of_seq (seq {for foo in mylist do if foo.Description <> "" then yield foo.Description}))
(String.Join should have an overload for string IEnumerables / seqs and not just accept string arrays.)


"The fact is that functional idioms work badly in languages that don't have syntactic support for them."

Then don't use those idioms. Work with your language, not against it. Learning Haskell taught the author to be more conscientious with state. Good. But nobody wants to debug imperative/OO code written by somebody newly infatuated with Haskell* -- it's like when people study a foreign language in high school and start dropping little fragments of Spanish/French/Japanese/etc. into casual conversation all the time, oblivious to whether the people around them understand it.

One of the comments (by "tom") nails it:

One of the more potent reasons for learning other modus operandi in programming to keep you from building tolerance to the same assumptions.

Learning several unrelated languages will almost certainly make you a better programmer, but code that assumes everybody else you're working with has the same background as you will be difficult to maintain. The language you're working in is the foundation for your shared culture, so do your best to write idiomatically in that.

* I have been guilty of this.


Interesting. It also happens with people newly infatuated with OO, esp design patterns.

I think this is a natural phase of learning - when you first grasp a new concept, you use it everywhere; later, you use it where appropriate. Maybe the first phase is exploratory practice, to grasp its limitations. I'm sure there is a pithy expression for these two phases.

Anyone know it?

The first phase is like "To the man with a hammer, everything looks like a nail". It's also similar to Fred Brooks' "Second System Effect" http://en.wikipedia.org/wiki/The_Mythical_Man-Month#The_Seco... , where you try to learn from all the mistakes you made in the First System.

Children learning language will apparently learn and use plurals correctly - until they discover the abstraction that you can add "s" to make a plural. They then apply it everywhere, including the irregular plurals: mans, foots, tooths, mouses etc. (that last one may be legit these days, mouse having acquired a technical referent). Or even like that developmental stage of babies, where they try chewing everything they find.


Agreed. I think the point of learning multiple paradigms is so that you can recognize when idiomatic programming in your preferred (or employer-mandated) language is not the best way to get the job done--recognizing when it's time to write functional-style code in Python, OO-style code in Haskell, or whatever. But most of the time, you follow the grain of the language.


Absolutely. Some languages comfortably support several paradigms, of course (http://www.c2.com/cgi/wiki?MultiParadigmProgrammingLanguage).


I think any non-toy language needs to make it non-painful to program in several paradigms, but I'm not sure about the "comfortably" part; it's good for a langauge to be designed around a central paradigm that can work for most tasks and that the implementor can optimize for. So Python is OO and Haskell is functional, and there are many problems that you solve in an OO style when writing Python and a functional style when using Haskell, and that's OK. And every once in a while you need to write functional-style code in Python, or OO-style code in Haskell, and that's OK too. I'm OK, you're OK, we're all OK....


Update: I probably should have made it more obvious for some people that the title of the post is not entirely serious, and mainly I'm just griping.

I think this explains the whole article and why there is no serious discussion here.


Comparing C# 2.0's functional capabilities to Haskell/Python is terribly misleading. You can perform functional tasks easily in 3.5 using lambda expressions and built in functional operators such as:

listOfTestObjects.ForEach(x => sOutputDescriptions += "\n" + x.Description());

http://weblogs.asp.net/scottgu/archive/2007/04/08/new-orcas-...


I hate to nitpick, but this isn't comparable to the author's code. You're including empty descriptions and your output string will begin with a newline. These seem like small details, but they make a big difference in the conciseness with which you can write the statement.


This is very true. I was not attempting to port the author's code to .NET 3.5 as much as to introduce C# lambda expression syntax to the readers here.


Yeah, I figured. I just didn't want others coming away with the impression that C# is more concise than Haskell or Python. I mean, I love lambdas as much as the next C# programmer, but there is still a fair bit of clunky syntax to deal with when using C#'s great new features.


The obvious answer is to switch to Brainfuck, the any at-work language, even Cobol, will be a pleasure to work in.


Actually, there are some serious points here. Consider this:

http://www.postmodernprogramming.org/stories/fixed_point_mad...


The title is "Why X makes you a worse progarmmer"

Then his conclusion: "I have no doubt that in general I am a better programmer for learning these languages..."

Um, hello?


He has a subtler point:

While learning languages X and Y will make you a better programmer in the long term, due to the added perspective they give you, keep in mind that this experience doesn't necessarily apply immediately and directly to every language you work in - don't try to force alien idioms from your new language of fascination into your day-to-day work language, because it's probably a much greater stretch than you realize. It will instead make your code incredibly awkward for everybody else to maintain, actually making you a worse programmer. (This is my understanding, but speaking as someone who has been guilty of doing this in the past, it's really something to be mindful about.)


Update: I probably should have made it more obvious for some people that the title of the post is not entirely serious, and mainly I'm just griping.


Or you could have titled it "Why using X makes me a worse software engineer" seeing as that's your conclusion.


...when programming in less powerful languages, because they start to seem inadequate.

I constantly find myself wanting to use idioms from these languages, or noticing how much less code I'd be able to write if I was using one of these languages.


C# was, to me, the best available language in TopCoder competitions. Once I had more than a passing acquaintance with Python, Lisp, Ruby and Haskell, it was annoying to compete in it. I spent a bunch of time trying to imagine a way to write in a language of my choice using code generation. Eventually I just gave up, and stopped playing (though this was one of many contributing factors)


C# feels dreadfully underdesigned to me. Despite its massive flaws, I still love Java for how consistent it is, and for how well-thought out it is. For instance, generics in C# aren't getting covariance and contravariance until 4.0, which I consider to be a fatal flaw. "List<? extends String>" makes so much more sense than "List<out T> where T : string".

That said, I am sad that the BGGA closures proposal won't make the cut for Java 7. I hope that some IDEs and APIs support it anyway.


I tended to use fairly flat data structures in my own code, so the extra hassle with generics didn't often come up. I did, however, use delegates (closures!) all over the place. They're verbose, but I mapped the delegate definitions to a resharper macro and it only took two keystrokes. That helped while writing code. It still was a pain to read, though, and ultimately the macro language wasn't good enough to do what I wanted.

The thing I like about Fortress is that it's a grown and growable language, with almost all constructs first class and exposed. Just like Lisp. But it's coming from the complete opposite direction in terms of syntax. Lisp barely has any syntax. Fortress gives you the most powerful production parser in a programming language, plus extremely adaptable syntax (where spaces can be operators!) plus a wicked way to render code, plus the ability to make your programs platform portable, adaptable, and parallelizable. It can be looked at as a programming language, true. But it can also be fruitfully imagined as a powerful framework for building one's own programming language. This is what I like to do with it. :-)


Enthusiasts always put their language of choice on a pedestal. Python is a very good language, but I often find it to be too restrictive and will use Ruby/Perl for elegant metaprogramming and Erlang for functional programming.


You completely missed the point of the article. It isn't about why Python & Haskell are good or bad languages, it's about how learning them doesn't necessarily improve ones' ability to work on projects with other developers, in unrelated languages.

There are social factors in language choice, it isn't just about the quality of the language itself.


And my point is that every language is flawed. If you only focus on the unique features of the languages you learn, you may not become a better program when working with languages that lack these features. But simply assimilating these ideas will do quite a bit to improve your productivity as well as your wisdom when designing systems because you will be alert for inadequacies that you would have missed before.


I agree with you.


And I wish I had a silent bicycle...


As others have said, stop fighting the language and embrace it. For the Java haters, here's a Java one-liner that is not much longer (though I'll admit, uglier) than all the rest:

result = join(new ArrayList<String>(){{ addAll(list); while(remove("")); }},"\n");

And as a bonus - for the extra few characters you typed, this will fail at compile time instead of runtime when the list turns out to contain FooBars instead of strings.


Missing the application of Foo.description(). That said, I feel that compile-time failure is vastly underrated. Java is making even more progress on that front by introducing type checker frameworks with JSR 308.


So why not use IronPython or F#?


This article is two years old, at which point F# wasn't much more than a research project.


That's an extremely rash claim: I'm struggling to adapt to a different language, and so will you.


That's not what the article says.


Alternate title: Another article demonstrating that trolling works.


The title is catchy but imprecise. After discovering the concision of python and haskell, the author has less tolerance for the verbosity of c#, the language with which he earns his living. Perhaps the solution is to earn a living with a more concise language.


The moral of the story: don't bother improving yourself, unless you have the freedom to improve your environment accordingly.

That's the moral of the story? That's not a moral, that's whining.


I just wonder how I should be able to find out that the environment needs improving without improving myself first.


Don't improve? Seems immoral.


Agreed. Making a living with Haskell may be difficult in the world that we live in, but doing so with Python should not pose a serious challenge. There are still plenty of jobs out there for talented coders, but you'll always be stuck at the same job if you don't GO AND LOOK FOR A NEW JOB that allows you to wield your new-found power. Nobody's going to change your situation for you, that's something that you have to do for yourself.


Reminds me of a time I screwed up a Java project (well, not really; just took a long time to write code most Java devs wouldn't understand) by using attempting to use generics and Java's static typing system to build-up an ML-esque ADT system.

4-ply parameterized types (e.g. string array list option) do not attractive siblings in Java-land.




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

Search: