Induction with data

an illustration in 5 languages

2025 James B. Wilson

abstract class JSONBlob
class JSONString extends JSONBlob {
   JSONString(String s) {...}
}
class JSONNumber extends JSONBlob {
   JSONNumber(Number n) {...}
}

\(\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₁ 42

Agda (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 
  | one

ML 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 = zero

Haskell / Idris / Agda

not b = match b with 
   | zero -> one
   |  one -> zero

OCaml

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 A

Haskell / Idris / Agda

type 'a list =
  | Nil 
  | Cons of 'a * 'a list

OCaml

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> {}
class JSON
case AsBoolean(b : Boolean)
case AsString(s : String)
case AsNumber(n : Number)
case AsArray(len : Int)( as : Vector[JSON, len ] )
case null
case AsObject( blob : JSON )
case UserTag(name : String)(blob : JSON)
case Append( head : JSON, tail : JSON )
\begin{aligned} b& :\mathbb{J}^0\to \mathbb{J}\\ s(x:\text{String})& :\mathbb{J}^0\to \mathbb{J}\\ n(x:\text{Number})& :\mathbb{J}^0\to \mathbb{J}\\ a_n & :\mathbb{J}^n\to \mathbb{J}\\ \text{null} & :\mathbb{J}^0\to \mathbb{J}\\ o &: \mathbb{J}^1\to \mathbb{J}\\ \text{tag}(\text{name}:\text{String}) & : \mathbb{J}\to \mathbb{J}\\ \text{Append} &:\mathbb{J}^2\to \mathbb{J} \end{aligned}

Induction

By James Wilson

Induction

Inductions is reasoning about data mixed with operations on that data.

  • 32