Laziness
Ubiquitous laziness means you can do things like define
fibs = 1 : 1 : zipWith (+) fibs (tail fibs)
But it also provides us with a lot of more subtle benefits in terms of syntax and reasoning.
For instance, due to strictness ML has to deal with the value restriction, and is very careful to track circular let bindings, but in Haskell, we can let every let be recursive and have no need to distinguish between val
and fun
. This removes a major syntactic wart from the language.
This indirectly gives rise to our lovely where
clause, because we can safely move computations that may or may not be used out of the main control flow and let laziness deal with sharing the results.
We can replace (almost) all of those ML style functions that need to take () and return a value, with just a lazy computation of the value. There are reasons to avoid doing so from time to time to avoid leaking space with CAFs, but such cases are rare.
Finally, it permits unrestricted eta-reduction (\x -> f x
can be replaced with f). This makes combinator oriented programming for things like parser combinators much more pleasant than working with similar constructs in a strict language.
This helps you when reasoning about programs in point-free style, or about rewriting them into point-free style and reduces argument noise.