isLeapYear :: Integer -> Bool
isLeapYear year =
if divisibleBy 100
then divisibleBy 400
else divisibleBy 4
where
divisibleBy d = year `mod` d == 0
Conditional expressions
A conditional expression (if … then … else …
) is a compound expression that uses a test to determine which of two alternatives to evaluate to.
Many other languages feature a similar construct, often termed 'ternary operator'.
They are also known as if
expressions.
When p
is some expression of type Bool
and t
and f
are any two expressions of the same type, then if p then t else f
will
- evaluate to
t
ifp
evaluates toTrue
, and - evaluate to
f
ifp
evaluates toFalse
.
Conditional expressions are syntactic sugar for certain case
expressions:
_ = if p then t else f
-- is an abbreviation of
_ = case p of
True -> t
False -> f
In this approach
This approach uses exactly two tests to determine whether a year is a leap year.
The first test is for divisibility by 100. Once we know if the year is a multiple of 100, we know which further test to perform.
- If the year is evenly divisible by 100, then
divisibleBy 100
will evaluate toTrue
and the entireif
expression will evaluate to whateverdivisibleBy 400
evaluates to. - If the year is not evenly divisible by 100, then
divisibleBy 100
isFalse
and so theif
expression evaluates todivisibleBy 4
.
When to use if
?
if
expressions might be a good fit when you
- need an expression that
- chooses between exactly two options
- depending on a single
Bool
.
When you have something other than a Bool
, use case
instead.
When you do not strictly need an expression, an alternative is to use guards.
When you need to choose between more than two options, guards might be the solution.
However, guards are not expressions and so are not always applicable.
In such cases you might want to break out a multi-way if
, available through the MultiWayIf
language extension:
{- LANGUAGE MultiWayIf -} -- at the top of the file
_ = if | condition -> expression
| proposition -> branch
| otherwise -> alternative
-- which is syntactic sugar for
_ = case () of
_ | condition -> expression
_ | proposition -> branch
_ | otherwise -> alternative
For more on this question, see Guards vs. if-then-else vs. cases in Haskell on StackOverflow.
An example of lazy evaluation
Just like 'ternary operators' in other languages, conditional expressions evaluate lazily. Specifically, only the relevant branch is evaluated:
ghci> error "Crash!" -- for demonstration
*** Exception: Crash!
ghci> if even 42 then "Success!" else error "Crash!"
"Success!"
Notice how evaluating the entire if
expression does not result in a crash, even though one of its branches would if it were evaluated.
In our solution above we have
year | divisibleBy 100 |
divisibleBy 400 |
divisibleBy 4 |
is leap year |
---|---|---|---|---|
2020 | False |
not evaluated | True |
True |
2019 | False |
not evaluated | False |
False |
2000 | True |
True |
not evaluated | True |
1900 | True |
False |
not evaluated | False |