Is it me, or was it a bad example for where Haskell "really shines"? I don't like the "use Haskell cause it has these really cool functions" approach. Feels so superficial.
Having strong and concise type guarantees is great. Haskell's a great example of that. So is Ocaml.
fibs :: [Integer]
fibs = 0 : 1 : zipWith (+) fibs (tail fibs)
Even still, it's difficult to get the feel of actually using the language from one function taken from a freshman year CS class. powerset :: [a] -> [[a]]
powerset xs = filterM (\x -> [True, False]) xs
ghci> powerset [1,2,3]
[[1,2,3],[1,2],[1,3],[1],[2,3],[2],[3],[]]
Edit: I read LYaH some time ago but this example stuck with me because it's both wonderful and mind-blowing. powerset :: [a] -> [[a]]
powerset [] = [[]]
powerset (x:xs) = yss ++ map (x:) yss
where yss = powerset xs
> powerset [1,2,3]
[[],[3],[2],[2,3],[1],[1,3],[1,2],[1,2,3]]Haskell is worth the effort, but I don't think the article's author did a great job of articulating why.
Some things I've noticed:
Haskell does not automatically admit an extra "null" value to every type, so you can ordinarily ignore NullPointerExceptions and the like. When you do need a nullable bit of data, you have to do some extra work: You must rename the type from "Foo" to "Maybe Foo" and change all the callsites. These callsites must test for null and specify what happens. (there exists a non-terminating explode() function if you really want it) The build will fail until every last case is covered. This is great for reliability.
Most data in Haskell is immutable. This encourages good programming practice, sure, but it really pays off when you start writing parallel code. Parallel Haskell is by far the easiest way I know to write fast concurrent stuff. On that note, Haskell's concurrency primitives are very well-designed.
Haskell's solution to callback hell is more general and powerful than anything I know. The "do" block is really just super elegant syntax sugar for a CPS chain.
Our unit test suite does not intermittently fail. We restrict side effects such that our build will fail if a test tries to do non-faked I/O. It took a bit of effort to build, but normal use is effortless.