1. 13

  2. 4

    I’ve tried Haskell a few times now and I really want to like it, but I just can’t stand it.

    It seems like creating anything real in Haskell requires doing it twice. Once in the pure, lazy, Haskell way, and then a second time using unsafePerformIO, subverting laziness, and replacing all the pure, lazy stdlib data structures with mutable third party versions to get any kind of decent performance.

    In theory, I think Haskell is awesome. In practice it just doesn’t live up to my expectations.

    1. 5

      requires doing it twice

      More the case when you’re first learning. You learn to anticipate what you’ll want with experience and the laziness is a non-issue for most Haskell code out there. Much like how you learn patterns, anticipate the application of approaches in the languages you know.

      Whole-heartedly worth it for me, the complexity management (able to scale to larger projects), runtime efficiency, compiled binaries, Best-of-our-industry concurrency kit, best type system that isn’t costly time wise, etc etc

      I came to Haskell from Clojure. I spent more time futzing around with perf and laziness in Clojure than I do Haskell :)

      1. 6

        I guess the problem I have is that in most languages, applying known patterns and optimizations doesn’t mean avoiding the features the language is known for.

        In Common Lisp, when I need to optimize a function, I don’t rewrite it to be more like C, or stop using some Common Lisp features - I add type declarations, turn on optimizations, and retry. Then maybe I try a different algorithm, or look for ways to cache more stuff, etc. I can have code with good Common Lisp style that is also fast.

        This is even more true with C and C++ and some other languages.

        In Haskell the goto optimization techniques are to get rid of most of the stuff that makes Haskell unique. It’s too slow, so let’s turn off laziness here. Or let’s use unsafePerformIO. Or let’s replace builtin strings with a library that uses mutable arrays and unsafePerformIO behind the scenes. The one technique that isn’t a cop out is making things run in parallel, but that’s not Haskell specific. It seems that in large part there’s good Haskell code, and then there’s optimized Haskell that breaks all the regular coding rules. And to avoid getting chastised by code reviewers and people in IRC and mailing lists, you’re only allowed to write the second kind after you’ve written the first kind and found out it wasn’t fast enough.

        My point is, it would be nice to see some articles like this where the solution stays within canonical Haskell, because that’s what’s advertised by the Haskell community.

        1. 4

          My point is, it would be nice to see some articles like this where the solution stays within canonical Haskell

          Using the right data structure for the job is one example of this. (List -> Vector, using real graph library rather than homespun adjacency list, matrix libs)

          I don’t think any Haskellers see selective application of strictness or unpacking as not being Haskell. Lazy is the right default, but sometimes strictness is wanted. There’s no uniquely Haskell-y way to write Haskell. Haskell is an ensemble language of multiple paradigms and tools for solving problems. It’s the combination thereof that is Haskell. Consider our concurrency/synchronization primitives. Nobody has anything that rich yet also performant and easy to use.

          Speaking personally, I can’t write reliable concurrent software unless I’m using Haskell!

          Example. You are trying to write a streaming parser. Lets say you did it with Strings and lazy functions. Probably not great perf-wise. Why?

          1. Well, the laziness might not be appropriate - you want to immediately force the thunks as you process the data. So you want a streaming library. Like conduit or pipes.

          2. You used String. String is a linked list…don’t do that :) - so you use bytestring.

          3. You homespun your own parser lib. Don’t do that. So you use Attoparsec.

          Then you realize libraries like this or this do what you wanted to begin with.

          Writing fast code in Haskell, for most beginners, is about knowing what libraries and abstractions to use. I don’t know anybody that has write to “dons” style code as you see in the shootout. The numerics stack somebody is writing for Haskell is infinitely more Haskell-y than that.

          80% of the perf advice I end up giving people boils down to “stop using List/String (Which is List of Char)”.

        2. 2

          You know what, I am constantly realizing that the more things one knows, the more likely is to isolate himself into “language esotericisms” that newcomers find difficult. There is a benefit to lazy as well as eager evaluation. That’s why we have more languages. Haskell is a very practical starting point for the world of functional programming, even with its pitfalls :)

        3. 3

          I’ve sunk my teeth in to Haskell and, coming from Common Lisp, my biggest hurdle so far is: syntax :-)

          But I guess it’ll grow on me. It does make picking up the language quite a bit harder though, having to look up every other symbol, so I hope it is worth it in the end.

          1. 4

            It has been very much worth it for me (I’m an ex-lisper).

            I wrote a guide for learning Haskell, I’d be pleased if it can help at all: https://github.com/bitemyapp/learnhaskell

            1. 2

              I saw you mention it before and I’ve been going through it already, thanks!

              (I can’t see myself become an ex-Lisper though.)

              1. 2

                Awesome, hope you enjoy it. :)