These functions are mutually recursive (each one can call the other one), with base cases. Lets follow an example evaluation using isOdd
. First, I will start by changing the guards into the equivalent if
s for (hopefully) more clarity in this answer (though I would usually suggest using guards).
isOdd, isEven :: Int -> Bool
isOdd n =
if n <= 0
then False
else isEven (n-1)
isEven n =
if n < 0
then False
else if n == 0
then True
else isOdd (n-1)
Now, we can try stepping through an example evaluation[1]:
isOdd 3
==> if 3 <= 0 (Applying isOdd to 5 and expanding the body)
then False
else isEven (3-1)
==> isEven (3-1) (3 > 0)
==> if 2 < 0
then False
else if 2 == 0
then True
else isOdd (2-1)
==> isOdd (2-1) (4 > 0, so the first two branches aren't taken)
==> if 1 <= 0 (Applying isOdd to 1 and expanding the body)
then False
else isEven (1-1)
==> isEven 0
==> if 0 < 0
then False
else if 0 == 0
then True
else isOdd (0-1)
==> True (0 == 0, so the second branch is taken)
The intuition behind why these functions work is this: if a non-negative integer (natural number) n
is greater than 0
, it is odd if its predecessor (n-1
) is even and it is even if its predecessor is odd. This is true since even and odd numbers alternate.
I would recommend stepping through an evaluation whenever you run into a function (or, in this case, pair of functions) that you don't understand using a small example like this.
[1]: Note for something that doesn't really matter for this question: I've slightly simplified when the expressions of the shape x-1
get reduced to the corresponding number.