I liked Haskell when I tried it for all of these reasons. However, there were a few problems I had:
1. Debugging logic. I LOVE its typesystem, which catches most of my errors. However, sometimes I have algorithmic/logic errors. In a perfect world, I would have worked those out well before coding. But once I have coded it, I just don't know how to debug my code. I have gotten so used to gdb/pdb as my last resort that I am lost without it. Does anybody have any idea debugging Haskell programs?
2. Its Lazy evaluation sometimes get in the way of tail recursion optimization unless you force it, and can lead to the stack getting full. But this can be handled by forcing evaluation with seq.
If you want "print-based" debugging, you can use Trace.Utils.traceAround:
processValue f =
traceAround "processValue" $
after .
map f .
before
Or, if you just want the results of "after":
processValue f =
traceId "Result of after: " .
after .
map f .
before
Alternatively, there's also the FileLocation package, which gives you filenames + line numbers in your debug prints via Template Haskell macros.
2. Controlling evaluation is indeed tricky. Haskell programmers take manipulation of infinite data structures (of any kind) for granted -- but then if they want to control stack use, it gets a bit tricky. Other languages take stack use management for granted, but if you want to have infinite arbitrary data structures, it gets tricky.
I personally prefer Haskell's choice, because it means that having infinite structures doesn't contaminate the code with effects (which explicit suspensions/resumptions of computations would do), but it could probably be done better (e.g: move laziness/strictness annotations mostly to the type-level).
For debugging my haskell code I first determine if the bug is in a pure function or IO. If it's in IO I use typical print type debugging (either running the IO action in ghci or just rerunning my program repeatedly).
If it's in a pure function, I load the code up in ghci and use it to replicate and explore the inputs that lead to the bug, and try out fixes. In a complex function I'll often promote (de-indent) any 'where/let' helpers to toplevel definitions so I can try them in ghci and narrow down which is behaving badly.
I have not missed gdb in haskell, but then most of my gdb experiences have involved asking for a stack trace and discovering the stack's been smashed by the bug. Or using gdb to chase down some unexpected mutation of a variable deep in the code. Not problems typical to haskell.
This looks like some interesting research in the right direction for debugging lazy programs, http://arxiv.org/pdf/1108.4706v1. Which goes to show, people need to be looking at Racket a lot more then they do - including Haskellers.
About 1: Yes debugging (with a debugger) is more complicated than in strict languages. For most problems, you can however use QuickCheck to test your algorithms and logic. QuickCheck takes some laws about your functions, and gives you the simplest test cases that break those laws (if they are any). Very illuminating.
QuickCheck is not a replacement for a debugger. But it allows you to go further without one.
2. I agree. If you have a space leak because of too much laziness, you have to add strictness annotations until the problem goes away. Some people have better intuition than others for that, but there doesn't seem to be an easy formal way to decide.
1. Debugging logic. I LOVE its typesystem, which catches most of my errors. However, sometimes I have algorithmic/logic errors. In a perfect world, I would have worked those out well before coding. But once I have coded it, I just don't know how to debug my code. I have gotten so used to gdb/pdb as my last resort that I am lost without it. Does anybody have any idea debugging Haskell programs?
2. Its Lazy evaluation sometimes get in the way of tail recursion optimization unless you force it, and can lead to the stack getting full. But this can be handled by forcing evaluation with seq.