The Problem
For a good answer, we first need a specific question. Consider that natural numbers that are either zero Z
or the successor S n
of another natural number n
.
data Nat = Z | S Nat
zero = Z
one = S Z
two = S $ S Z
In Haskell we can also write infinite recusive data structures like
infinity = S infinity
As long as a single Nat
number isn't infinity
we can determine if it is even or odd.
even :: Nat -> Bool
even Z = True
even (S n) = odd n
odd :: Nat -> Bool
odd Z = False
odd (S n) = even n
For finite Nat
s, even
is either True
or False
and even infinity
is undefined
. This is ok, but what if we wanted to check if either of two numbers is even
? We can write a naïve implementation:
eitherEven :: Nat -> Nat -> Bool
eitherEven x y = even x || even y
This does quite well whenever the first argument is finite. In the following, even
is any even number and odd
is any odd number.
eitherEven even even == True
eitherEven even odd == True
eitherEven even infinity == True
eitherEven odd even == True
eitherEven odd odd == False
eitherEven odd infinity == undefined
But when the first argument is infinite, it doesn't return True
even when the second argument is Even
eitherEven infinity even == undefined -- this should be True
eitherEven infinity odd == undefined
eitherEven infinity infinity == undefined
A simple solution
A simple solution to the problem is to alternate between testing the first argument and testing the second argument. When we call the function recursively, we swap the arguments to alternate which of the two arguments is being tested.
eitherEven :: Nat -> Nat -> Bool
eitherEven Z _ = True
eitherEven (S Z) y = even y
eitherEven (S (S x)) y = eitherEven y x
This has the desired output even when the first argument isn't finite.
> eitherEven infinity two
True
For more complicated problems where the arguments aren't treated symmetrically, you can pass a Bool
flag and flip it on each step. In general you can use any state machine to keep track of where you are working.
This solution isn't very satisfying because it doesn't immediately solve what to do when we want to test if any of three numbers is even. To do so we need to write a new function.
anyEven3 :: Nat -> Nat -> Nat -> Bool
anyEven3 Z _ _ = True
anyEven3 (S Z) y z = eitherEven y z
anyEven3 (S (S x)) y z = anyEven3 y z x
We put x
at the end because we want to try both y
and z
before trying x
again. We're making a queue. If we can prove that the first thing in the queue produces a result, True
, we are done. If we can prove the first thing in the queue doesn't produce a result, we remove it from the queue and use a version that works on a smaller set of inputs. If we can't decide the result for the first thing in the queue, we put it at the end. The same pattern can be seen in eitherEven
which takes two arguments and even in even
which takes only one argument.
anyEven :: [Nat] -> Bool
anyEven = go []
where
go [] [] = False
go r [] = go [] (reverse r)
go r ( Z :_ ) = True
go r ( S Z :ns) = go r ns
go r ((S (S x)):ns) = go (x:r) ns