module Even
% default total
% auto_implicits on
% access public export
{- Predicates as Boolean-Valued Functions -}
-- evenness as a boolean-valued function:
is_even : Nat -> Bool
is_even Z = True
is_even (S Z) = False
is_even (S (S n)) = is_even n
four_is_even : Bool
four_is_even = is_even 4
six_is_even : Bool
six_is_even = is_even 6
-- From the perspective of propositions as Booleans,
-- there are only two propositions, True and False.
true_is_true : Bool
true_is_true = four_is_even == six_is_even
-- Fermat's Last Theorem and 1 + 1 == 2 have the same value.
-- A different perspective is that each logical proposition is distinct,
-- and that a proof of a proposition is a conceptual object in its own right.
{- Predicates as Indexed Types -}
-- evenness as an Nat-indexed type:
data Even : (n : Nat) -> Type where
Z_even : Even Z -- primitive evidence that 0 is even
SS_even : Even n -> Even (S (S n)) -- primitive evidence that the
-- double successor of an even is even
{-
SS_even
SS_even (SS_even
SS_even (SS_even (SS_even
Z_even Z_even Z_even) Z_even))
0 |---> 1 |---> 2 |---> 3 |---> 4 |---> 5 |---> 6 ...
S S S S S S
-}
-- We can construct elements of type Even n only when n is even,
-- and each Even (2 * m) contains exactly one element.
four_even : Even 4
four_even = SS_even (SS_even Z_even)
six_even : Even 6
six_even = SS_even (four_even)
-- We can think of a term of type Even n as a proof that n is even,
-- and we can manipulate these proofs just like any other expressions in our programs.
{- Proving Properties by Constructing Terms -}
{- On Dependent Pattern Matching
- A term of type Even n acts a proof that n is even.
- A variable of type Even n acts as an assumption that n is even.
- We can use pattern matching to examine the forms that such an assumption can have.
- Because the type Even n is indexed by the type Nat, case analyzing
a proof of Even n gives us information about the Nat n as well.
-}
-- The constructor SS_even says that the double-successor of an even number is even.
-- We can prove the converse as well:
pp_even : Even (S (S n)) -> Even n
pp_even (SS_even n_even) = n_even
-- even plus even is even:
-- (We do induction on the first proof because Nat addition
-- is defined by recursion on its first argument.)
even_plus_even : Even m -> Even n -> Even (m + n)
even_plus_even Z_even n_even = n_even
even_plus_even (SS_even m_even) n_even = SS_even (even_plus_even m_even n_even)
{- On Proof Engineering
- For a variety of reasons, Idris may not show you all the information you may want
about an intermediate proof state.
- We will learn about a variety of strategies to deal with this.
Today we focus on two.
-}
-- even times even is even:
-- by the "lemma" strategy:
namespace by_lemma
private
even_times_even : Even m -> Even n -> Even (m * n)
even_times_even Z_even n_even = Z_even
even_times_even {m = (S (S m))} (SS_even m_even) {n = n} n_even =
even_plus_even n_even n_plus_m_times_n_even
where
m_times_n_even : Even (m * n)
m_times_n_even = even_times_even m_even n_even
--
n_plus_m_times_n_even : Even (n + (m * n))
n_plus_m_times_n_even = even_plus_even n_even m_times_n_even
% hide even_times_even -- so we can reuse the name
-- by the "explication" strategy:
namespace by_explication
private
even_times_even : Even m -> Even n -> Even (m * n)
even_times_even Z_even n_even = Z_even
even_times_even {m = (S (S m))} (SS_even m_even) {n = n} n_even =
even_plus_even {m = n} {n = n + (m * n)} n_even
(even_plus_even {m = n} {n = m * n} n_even (even_times_even m_even n_even))
% hide even_times_even -- so we can reuse the name
-- cleaned up version:
even_times_even : Even m -> Even n -> Even (m * n)
even_times_even Z_even n_even = Z_even
even_times_even (SS_even m_even) n_even =
even_plus_even n_even (even_plus_even n_even (even_times_even m_even n_even))