Induction for proofs
an illustration in 5 languages
2025 James B. Wilson

Dedekind-Peano Induction
\(P(0)\)
\(P(k)\to P(++k)\)
\(n:\mathbb{N}\)
\(P(n)\)
"Base Case"
"Induction Hypothesis"
"Number to use"
"Consequence"
1 is odd.
If k is odd then k+2 is odd.
4=2+2
Nothing to say, 4=2+2 doesn't match the induction hypothesis nor the base case.
1 is odd.
If k is odd then k+2 is odd.
5=(1+2)+2
If 1+2 is odd then (1+2)+2 is odd.
If 1 is odd then 1+2 is odd.
1 is odd.
5 is odd.

// Form the type
sealed trait List[A]
// Introduce base case
case [] extends List[A]
// Introduct Induction Hypothesis
case (head : A) :: (tail : List[A]) extends List[A]
def length( as : List[A]) : Int =
// Eliminate inductive type
as match {
// Compute base case, 0 lenght
case [] -> 0
// Compute Resursion via "Inductive Hypothesis":
// if length(tail)=k then length(head::tail) = ++k
case head :: tail -> ++length(tail)
}Strong Induction
\(P(0)\)
\(P(0),....,P(k)\to P(++k)\)
\(n:\mathbb{N}\)
\(P(n)\)
"Base Case"
"Induction Hypothesis"
"Number to use"
"Consequence"
A triangle has 180 degrees.
If k-gon has \(180(k-2)\) degree then a (k+1)-gon has \(180((k+1)-2)\) degrees.
n=5=++(++3)=(3+1)+1

// Form the type
sealed trait List[A]
// Introduce base case
case [] extends List[A]
// Introduct Induction Hypothesis
case (head : A) :: (tail : List[A]) extends List[A]
def length( as : List[A]) : Int =
// Eliminate inductive type
as match {
// Compute base case, 0 lenght
case [] -> 0
// Compute Resursion via "Inductive Hypothesis":
// if length(tail)=k then length(head::tail) = ++k
case head :: tail -> ++length(tail)
}Noetherian Induction
Every subset \(F\) of events \(E\) has minimal ones \(\partial E\)
If \(P(\partial F)\) then \(P(F)\)
\(P(\partial E)\)
\(e\in E\)
\(P(e)\)
"Boundary Case"
"Induction Hypothesis"
"Event to use"
"Consequence"
"Well-founded"
E=Computer network.
If you can hack one user then you can hack his inbox.
Someone hacked you.
Your entire network is hacked; and you have just been fired.
def guess(n) =
n match {
case Even -> return "You loose"
case Odd -> guess(n div 3)
case Zero -> return "You win!"
}Guess.
> guess(6) You loose
> guess(5) guess(1) guess(0) You loose
> guess(0) You loose
def guess(n) =
n match {
case Zero -> return "You win!"
case Even -> return "You loose"
case Odd -> guess(n div 3)
}Guess.
> guess(6) You loose
> guess(5) guess(1) guess(0) You win
> guess(0) You win
\(\vdash \mathbb{B}:\text{Set}\) (Form\(_{\mathbb{B}}\))
\(\vdash 0:\mathbb{B}\) (Intro\(_{0-\mathbb{B}}\))
\(\vdash 1:\mathbb{B}\) (Intro\(_{1-\mathbb{B}}\))
\(\begin{array}{rl} \Gamma & \vdash b:\mathbb{B}\\ \Gamma & \vdash x:P(0)\\ \Gamma & \vdash y:P(1)\\ \hline \Gamma & \vdash \text{if }b \text{ then} : P(b)\end{array}\) (Elim\(_{\mathbb{B}}\))
\(\begin{array}{rl} \Gamma & \vdash 0:\mathbb{B}\\ \Gamma & \vdash x:P(0)\\ \Gamma & \vdash y:P(1)\\ \hline \Gamma & \vdash \text{if }b \text{ then} := x\end{array}\) (0-Comp\(_{\mathbb{B}}\))
open import Data.Nat using (ℕ)
open import Data.String using (String)
open import Data.Sum using (_⊎_; inj₁; inj₂)
module Bits where
-- Binary digits up ↑ and down ↓
data 𝔹 : Set where
↑ : 𝔹
↓ : 𝔹
-- Use of a bit
asFive : 𝔹 → ℕ ⊎ String
asFive ↑ = inj₁ 5
asFive ↓ = inj₂ "Five"\(\begin{array}{rl} \Gamma & \vdash 1:\mathbb{B}\\ \Gamma & \vdash x:P(0)\\ \Gamma & \vdash y:P(1)\\ \hline \Gamma & \vdash \text{if }b \text{ then} := y\end{array}\) (1-Comp\(_{\mathbb{B}}\))
Agda (1999) (a Haskell family 1990)
open import Data.Nat using (ℕ)
open import Data.String using (String)
open import Data.Sum using (_⊎_; inj₁; inj₂)
module Bits where
-- Binary digits up ↑ and down ↓
data Trit : Set where
a : Trit
b : Trit
c : Trit
-- Do something
thing : Trit → ℕ ⊎ String
thing a = inj₁ 5
thing c = inj₂ "Five"
thing _ = inj₁ 42Agda (1999) (a Haskell family 1990)
\(\vdash \mathbb{B}:\text{Set}\) (Form\(_{\mathbb{B}}\))
\(\vdash 0:\mathbb{B}\) (Intro\(_{0-\mathbb{B}}\))
\(\vdash 1:\mathbb{B}\) (Intro\(_{1-\mathbb{B}}\))
\(\begin{array}{rl} \Gamma & \vdash b:\mathbb{B}\\ \Gamma & \vdash x:P(0)\\ \Gamma & \vdash y:P(1)\\ \hline \Gamma & \vdash \text{if }b \text{ then} : P(b)\end{array}\) (Elim\(_{\mathbb{B}}\))
\(\begin{array}{rl} \Gamma & \vdash 0:\mathbb{B}\\ \Gamma & \vdash x:P(0)\\ \Gamma & \vdash y:P(1)\\ \hline \Gamma & \vdash \text{if }b \text{ then} := x\end{array}\) (0-Comp\(_{\mathbb{B}}\))
(* Binary digits up and down *)
type bit =
| Up
| Down
(* Sum type for either natural number or string *)
type nat_or_string =
| Left of int
| Right of string
(* Use of a bit. *)
let asFive = function
| Up -> Left 5
| Down -> Right "Five"
\(\begin{array}{rl} \Gamma & \vdash 1:\mathbb{B}\\ \Gamma & \vdash x:P(0)\\ \Gamma & \vdash y:P(1)\\ \hline \Gamma & \vdash \text{if }b \text{ then} := y\end{array}\) (1-Comp\(_{\mathbb{B}}\))
OCaml (1996) (an ML family 1973)
\(\vdash \mathbb{B}:\text{Set}\) (Form\(_{\mathbb{B}}\))
\(\vdash 0:\mathbb{B}\) (Intro\(_{0-\mathbb{B}}\))
\(\vdash 1:\mathbb{B}\) (Intro\(_{1-\mathbb{B}}\))
\(\begin{array}{rl} \Gamma & \vdash b:\mathbb{B}\\ \Gamma & \vdash x:P(0)\\ \Gamma & \vdash y:P(1)\\ \hline \Gamma & \vdash \text{if }b \text{ then} : P(b)\end{array}\) (Elim\(_{\mathbb{B}}\))
\(\begin{array}{rl} \Gamma & \vdash 0:\mathbb{B}\\ \Gamma & \vdash x:P(0)\\ \Gamma & \vdash y:P(1)\\ \hline \Gamma & \vdash \text{if }b \text{ then} := x\end{array}\) (0-Comp\(_{\mathbb{B}}\))
// Binary digits up and down
sealed trait 𝔹
case object ↑ extends 𝔹
case object ↓ extends 𝔹
// Use of a bit
def asFive(bit: 𝔹): Either[Int, String] = bit match {
case ↑ => Left(5)
case ↓ => Right("Five")
}\(\begin{array}{rl} \Gamma & \vdash 1:\mathbb{B}\\ \Gamma & \vdash x:P(0)\\ \Gamma & \vdash y:P(1)\\ \hline \Gamma & \vdash \text{if }b \text{ then} := y\end{array}\) (1-Comp\(_{\mathbb{B}}\))
Scala (2004) (an Java family 1995)
\(\vdash \mathbb{B}:\text{Set}\) (Form\(_{\mathbb{B}}\))
\(\vdash 0:\mathbb{B}\) (Intro\(_{0-\mathbb{B}}\))
\(\vdash 1:\mathbb{B}\) (Intro\(_{1-\mathbb{B}}\))
\(\begin{array}{rl} \Gamma & \vdash b:\mathbb{B}\\ \Gamma & \vdash x:P(0)\\ \Gamma & \vdash y:P(1)\\ \hline \Gamma & \vdash \text{if }b \text{ then} : P(b)\end{array}\) (Elim\(_{\mathbb{B}}\))
\(\begin{array}{rl} \Gamma & \vdash 0:\mathbb{B}\\ \Gamma & \vdash x:P(0)\\ \Gamma & \vdash y:P(1)\\ \hline \Gamma & \vdash \text{if }b \text{ then} := x\end{array}\) (0-Comp\(_{\mathbb{B}}\))
import java.util.Objects;
class Bits {
// Binary digits up and down using sealed interface
sealed interface 𝔹 permits Up, Down {}
final class Up() implements 𝔹 {}
final class Down implements 𝔹 {}
// Sum type equivalent using sealed interface
sealed interface Either<L, R> permits Left, Right {}
record Left<L, R>(L value) implements Either<L, R> {}
record Right<L, R>(R value) implements Either<L, R> {}
// Use of a bit.
Either<Integer, String> asFive(𝔹 bit) {
return switch (bit) {
case Up up -> new Left<>(5);
case Down down -> new Right<>("Five");
};
}
}\(\begin{array}{rl} \Gamma & \vdash 1:\mathbb{B}\\ \Gamma & \vdash x:P(0)\\ \Gamma & \vdash y:P(1)\\ \hline \Gamma & \vdash \text{if }b \text{ then} := y\end{array}\) (1-Comp\(_{\mathbb{B}}\))
Java (since 1995,
but feature here added in 2021 (JDK 16))
\(\vdash \mathbb{B}:\text{Set}\) (Form\(_{\mathbb{B}}\))
\(\vdash 0:\mathbb{B}\) (Intro\(_{0-\mathbb{B}}\))
\(\vdash 1:\mathbb{B}\) (Intro\(_{1-\mathbb{B}}\))
\(\begin{array}{rl} \Gamma & \vdash b:\mathbb{B}\\ \Gamma & \vdash x:P(0)\\ \Gamma & \vdash y:P(1)\\ \hline \Gamma & \vdash \text{if }b \text{ then} : P(b)\end{array}\) (Elim\(_{\mathbb{B}}\))
\(\begin{array}{rl} \Gamma & \vdash 0:\mathbb{B}\\ \Gamma & \vdash x:P(0)\\ \Gamma & \vdash y:P(1)\\ \hline \Gamma & \vdash \text{if }b \text{ then} := x\end{array}\) (0-Comp\(_{\mathbb{B}}\))
#include <variant>
#include <string>
#include <iostream>
// Binary digits up and down
enum class 𝔹 {
↑, // up
↓ // down
};
// Sum type equivalent using std::variant
using EitherIntString = std::variant<int, std::string>;
// Use of a bit
EitherIntString asFive(𝔹 bit) {
switch (bit) {
case 𝔹::↑:
return 5;
case 𝔹::↓:
return std::string("Five");
}
// Should never reach here with complete switch
return 0;
}
\(\begin{array}{rl} \Gamma & \vdash 1:\mathbb{B}\\ \Gamma & \vdash x:P(0)\\ \Gamma & \vdash y:P(1)\\ \hline \Gamma & \vdash \text{if }b \text{ then} := y\end{array}\) (1-Comp\(_{\mathbb{B}}\))
C++ (1983, but pattern matching still not standard, alternatives shown!)
\(\vdash \mathbb{B}:\text{Set}\) (Form\(_{\mathbb{B}}\))
\(\vdash 0:\mathbb{B}\) (Intro\(_{0-\mathbb{B}}\))
\(\vdash 1:\mathbb{B}\) (Intro\(_{1-\mathbb{B}}\))
Logic: two independent possibilities.
data 𝔹 : Set where
↑ : 𝔹 -- Voltage up
↓ : 𝔹 -- Voltage down
Haskell Family (Agda, Idris)
type bit =
| zero
| oneML Family (OCaml)
sealed trait Bit
case Zero extends Bit
case One extends Bit
Scala
enum Bit {
ZERO, ONE;
}
Java (similar for C++)
abstract sealed class Bit {}
final class Zero() extends Bit {}
final class One() extends Bit {}
Java altneratives
public sealed class Bit
permits Zero, One {
...
}
\(\vdash \mathbb{B}:\text{Set}\) (Form\(_{\mathbb{B}}\))
\(\begin{array}{rl} \Gamma & \vdash b:\mathbb{B}\\ \Gamma & \vdash x:P(0)\\ \Gamma & \vdash y:P(1)\\ \hline \Gamma & \vdash \text{if }b \text{ then} : P(b)\end{array}\) (Elim\(_{\mathbb{B}}\))
{
}
\(\begin{array}{rl} \Gamma & \vdash 0:\mathbb{B}\\ \Gamma & \vdash x:P(0)\\ \Gamma & \vdash y:P(1)\\ \hline \Gamma & \vdash \text{if }b \text{ then} := x\end{array}\) (0-Comp\(_{\mathbb{B}}\))
\(\begin{array}{rl} \Gamma & \vdash 1:\mathbb{B}\\ \Gamma & \vdash x:P(0)\\ \Gamma & \vdash y:P(1)\\ \hline \Gamma & \vdash \text{if }b \text{ then} := y\end{array}\) (1-Comp\(_{\mathbb{B}}\))
\(\vdash 0:\mathbb{B}\) (Intro\(_{0-\mathbb{B}}\))
\(\vdash 1:\mathbb{B}\) (Intro\(_{1-\mathbb{B}}\))
not : Bit -> Bit
not zero = one
not one = zeroHaskell / Idris / Agda
not b = match b with
| zero -> one
| one -> zeroOCaml
def not( b : Bit ) : Bit =
b match {
case Zero() => One()
case One() => Zero()
} Scala
select(b) {
case Bit.ZERO -> Bit.ONE
case Bit.ONE -> Bit.ZERO
}
Java (similar for C++)
if ( b instanceof Zero )
return new One();
if ( b instanceof One )
return new Zero();Java altneratives
Bit not( Bit b ) {
return select( b ) {
case Zero() -> new One();
case One() -> new Zero();
}
}\(\begin{array}{rl} \Gamma & \vdash b:\mathbb{B}\\ \Gamma & \vdash x:P(0)\\ \Gamma & \vdash y:P(1)\\ \hline \Gamma & \vdash \text{if }b \text{ then} : P(b)\end{array}\) (Elim\(_{\mathbb{B}}\))
\(\begin{array}{rl} \Gamma & \vdash 0:\mathbb{B}\\ \Gamma & \vdash x:P(0)\\ \Gamma & \vdash y:P(1)\\ \hline \Gamma & \vdash \text{if }b \text{ then} = x\end{array}\) (0-Comp\(_{\mathbb{B}}\))
\(A:Set\vdash \text{List}[A]:\text{Set}\) (Form\(_{List}\))
\(\vdash []:List[A]\) (Intro\(_{List}\))
\(a:A, tail:List[A]\vdash a::tail:List[A]\) (Intro\(_{List}\))
Logic: Lists.
data List A where
nil : List A
cons : (a : A) -> (tail : List A) -> List AHaskell / Idris / Agda
type 'a list =
| Nil
| Cons of 'a * 'a listOCaml
sealed trait List[A+]
case Nil extends List[Any]
case Cons( a : A, tail : List[A]) extends List[A]
Scala
Java (similar for C++)
public sealed class List<A> {}
class final Nil() extends List<Class> {}
class final Cons(<A> a,
List<A> tail) extends List<A> {}
Induction for proofs
By James Wilson
Induction for proofs
Inductions is reasoning about data mixed with operations on that data.
- 14