Lazy evaluation is a common property of purely functional programming languages to 'win back performance', it works quite simple, you only evaluate an expression once you need it. For instance, consider in Haskell
if x == 1 then x + 3 else x + 2
In strict (eager) evaluation, if x indeed is equal to two, then x + 3 is evaluated and returned, else x + 2, in Haskell, no such thing happens, x + 3 is just composed unto the expression, for instance, say I have:
let x = if x == 1 then x + 3 else x + 2
Well, then I store that in a variable, but what if I never ever ever ever use that variable due to some other conditionals hmm? I wasted a very expensive integer addition on my CPU for nothing. (okay, in practise you don't win on this but you get the idea with larger expressions)
Then the question begs, why aren't all languages lazy?, well, the simple reason is that in purely functional languages, expressions are guaranteed to have no side-effects at all. If they had, we would have to evaluate them in the right order. That's why in most languages they are eagerly evaluated. In languages where expressions have no side effect, there is no risk in lazy evaluation so it's a logical choice to win back the performance they tend to lose on other areae.
Another interesting side effect is that if-then-else in Haskell is really a function of the type Bool -> a -> a -> a
. In Haskell this means that it takes one argument of the type Boolean, another of the any type, another of the same type as the first, and returns that type again. You don't run into infinite evaluation of different control branches because values are evaluated only when they are needed for, which usually is at the very end of the program when a huge expression has been composed and is then evaluated for the final result, discarding all things the compiler thinks are not needed for the end result. For instance if I divide a hugely complex expression by itself, it can just be substituted for '1' without evaluating both parts.
The difference is visible in Scheme, which is traditionally strictly evaluated, but there is a lazy variant called Lazy Scheme, in Scheme (display (apply if (> x y) "x is larger than y" "x is not larger than y"))
is an error because if
is not a function, it's a specialized syntax (though some say syntax is not special in Scheme at all) as it does not necessarily evaluates all its arguments, else we'd run out of memory if we tried to compute a factorial for instance. In Lazy Scheme that works just fine because none of those arguments is evaluated at all until a function really needs the result for evaluation to continue, like display.